Timeout in goroutines and Http requests

Issue

I am checking the status of a server. The server has a sleep of more than 15 seconds and I am checking for the timeout.

package main

import (
    "fmt"
    "net/http"
    "time"
)

var urls = []string{
    "http://site-centos-64:8080/examples/abc1.jsp",
    }

type HttpResponse struct {
    url      string
    response *http.Response
    err      error
}
var ch = make(chan *HttpResponse, 100) // buffered
var count int   
func asyncHttpGets(urls []string) []*HttpResponse {
    responses := []*HttpResponse{}
    count:=0
    timeout := make(chan bool, 100)
    for i:=0;i<500;i++{
        go func(){
                for _, url := range urls {
                    resp, err := http.Get(url)
                    count++;
                    go func() {
                    time.Sleep(1 * time.Second)
                    timeout <- true
                    }() 
                    ch <- &HttpResponse{url, resp, err}
                    if err != nil {
                        return
                    }
                    resp.Body.Close()
                }
        }()
    }
    for {
        select {
        case r := <-ch:
            responses = append(responses, r)
            if count == 500 {
                return responses
            }
        case <-timeout:
                fmt.Println("Timed Out")
                if count == 500 {
                return responses
            }
        }
    }
    return responses

}

func main() {
    now:=time.Now()
    results := asyncHttpGets(urls)
    for _, result := range results {
        fmt.Printf("%s status: %s\n", result.url,result.response.Status)
    }
    fmt.Println( time.Since(now))
}

But what is happening is that initially it prints a “Timed Out” but the last 150-200 requests show the “200 OK” status which it should not. Also when trying to do it for a 1000 times it shows “panic: runtime error: invalid memory address or nil pointer dereference”

Solution

You are doing the resp, err := http.Get(url) before you initiate the timeout goroutine.
This will cause everything to block until the response is ready, then send on both channels simultaneously.

Just move starting the timeout goroutine to the line before sending the request, and it will be fine. i.e.:

  for _, url := range urls {


                go func() {
                   time.Sleep(1 * time.Second)
                   timeout <- true
                   count++;
                }() 

                resp, err := http.Get(url)
                count++; //I think this is what you meant, right?
                ch <- &HttpResponse{url, resp, err}
                if err != nil {
                    return
                }
                resp.Body.Close()
            }

BTW try to use atomic increments to count, and maybe use a waitgroup, and a time.After channel instead of sleep.

Answered By – Not_a_Golfer

Answer Checked By – Senaida (GoLangFix Volunteer)

Leave a Reply

Your email address will not be published.