goroutines和Http请求超时

时间:2014-02-03 06:17:29

标签: go timeout goroutine

我正在检查服务器的状态。服务器的睡眠时间超过15秒,我正在检查超时。

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))
}

但正在发生的事情是,它最初打印出“超时”,但最后150-200个请求显示“200 OK”状态,它不应该。此外,当尝试执行1000次时,它显示“恐慌:运行时错误:无效的内存地址或无指针取消引用”

2 个答案:

答案 0 :(得分:2)

在启动超时goroutine之前,您正在执行resp, err := http.Get(url)。 这将导致所有内容阻塞,直到响应准备就绪,然后同时发送两个通道。

只需在发送请求之前将超时goroutine启动到该行,就可以了。即:

  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尝试使用原子增量进行计数,并且可能使用等待组和time.After通道而不是睡眠。

答案 1 :(得分:0)

如果您想避免将并发逻辑与业务逻辑混合,我编写了这个库https://github.com/shomali11/parallelizer来帮助您。它封装了并发逻辑,因此您不必担心它。

所以在你的例子中:

package main

import (
    "github.com/shomali11/parallelizer"
    "fmt"
)

func main() {
    urls := []string{ ... }
    results = make([]*HttpResponse, len(urls)

    options := &Options{ Timeout: time.Second }
    group := parallelizer.NewGroup(options)
    for index, url := range urls {
        group.Add(func(index int, url string, results *[]*HttpResponse) {
            return func () {
                ...

                results[index] = &HttpResponse{url, response, err}
            }
        }(index, url, &results))
    }

    err := group.Run()

    fmt.Println("Done")
    fmt.Println(fmt.Sprintf("Results: %v", results))
    fmt.Printf("Error: %v", err) // nil if it completed, err if timed out
}