http - golang http超时和goroutines累积

标签 http go goroutine

我使用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)

并删除关闭和 nils ch 的 defer block 。

参见:http://blog.golang.org/go-concurrency-patterns-timing-out-and

这是我认为正在发生的事情:

  1. 5秒超时后,timeoutHttpGet返回
  2. defer 语句运行,关闭 ch 并将其设置为 nil
  3. 它开始执行实际获取的 go 例程完成并尝试将其数据发送到 ch
  4. 但是 ch 为零,因此不会收到任何内容,从而阻止该语句完成,从而阻止 go 例程完成

我假设您正在设置 ch = nil ,因为在此之前,您会遇到运行时 panic ,因为当您尝试写入关闭的 channel 时会发生这种情况,如 the spec 所描述的。

将 ch 的缓冲区设置为 1 意味着 fetch go 例程可以向其发送数据而无需接收器。如果处理程序由于超时而返回,则所有内容稍后都会被垃圾收集。

关于http - golang http超时和goroutines累积,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20990332/

相关文章:

go - Go 是否提供与 Threadpool 等效的功能

mysql - 在子程序中处理数据库打开和关闭

go - goroutines中的请求时间

django - 在 App Engine 上使用 Django 处理 HTTP HEAD 请求的最佳实践

java - 如何在 NetBeans 中正确导入 Apache HttpClient?

pointers - 通过引用传递自定义 slice 类型

go - 一段任意结构的接口(interface)用作函数参数(golang)

java - 使用 HttpURLConnection 的摘要认证

javascript - 页面重新加载和图像缓存

logging - Golang go-workers自定义日志记录中间件?