我使用goroutines实现http.Get超时,然后我发现goroutines的数量一直在稳定增长,当达到1000左右时,程序将退出
码:
package main import ( "errors" "io/ioutil" "log" "net" "net/http" "runtime" "time" ) // timeout dialler func timeoutDialler(timeout time.Duration) func(network, addr string) (net.Conn, error) { return func(network, addr string) (net.Conn, error) { return net.DialTimeout(network, addr, timeout) } } func timeoutHttpGet(url string) ([]byte, error) { // change dialler add timeout support && disable keep-alive tr := &http.Transport{ Dial: timeoutDialler(3 * time.Second), DisableKeepAlives: true, } client := &http.Client{Transport: tr} type Response struct { resp []byte err error } ch := make(chan Response, 0) defer func() { close(ch) ch = nil }() go func() { resp, err := client.Get(url) if err != nil { ch <- Response{[]byte{}, err} return } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { ch <- Response{[]byte{}, err} return } tr.CloseIdleConnections() ch <- Response{body, err} }() select { case <-time.After(5 * time.Second): return []byte{}, errors.New("timeout") case response := <-ch: return response.resp, response.err } } func handler(w http.ResponseWriter, r *http.Request) { _, err := timeoutHttpGet("http://google.com") if err != nil { log.Println(err) return } } func main() { go func() { for { log.Println(runtime.NumGoroutine()) time.Sleep(500 * time.Millisecond) } }() s := &http.Server{ Addr: ":8888", ReadTimeout: 15 * time.Second, WriteTimeout: 15 * time.Second, } http.HandleFunc("/", handler) log.Fatal(s.ListenAndServe()) }
http://play.golang.org/p/SzGTMMmZkI
用1而不是0初始化chan:
ch := make(chan Response, 1)
并删除关闭并延迟ch的延迟块。
参见:http : //blog.golang.org/go-concurrency-patterns-timing-out- and
我认为这是正在发生的事情:
我假设您正在设置,ch = nil是因为在您这样做之前,您会遇到运行时恐慌,因为这是您尝试写入规范中所述的关闭通道时发生的情况。
ch = nil
给ch一个1的缓冲区意味着fetch go例程可以发送给它而无需接收者。如果处理程序由于超时而返回,则稍后将收集所有垃圾。