you are better than you think

golang http client 关闭重用连接两种方法

· by thur · Read in about 1 min · (205 Words)
golang http client

同事有个模块会定期获取宿主机上的所有容器信息,代码中使用http client 请求docker engine获取容器信息。运行后收到用户反馈,程序导致系统fd泄漏,修复后代码如下,比原来代码多了一句 req.Close=true

    client := &http.Client{
        Timeout: time.Duration(timeout) * time.Millisecond,
        Transport: &http.Transport{
            Dial: unixDial,
        },
    }

    req, err := http.NewRequest("GET", url, nil)
    if err != nil {
        return nil, fmt.Errorf("NewRequest(): url = %s, err = %s", url, err)
    }

    req.Close = true // fd leak without setting this
    resp, err := client.Do(req)
    ...

因为我自己有个清理容器数据的代码也有类似逻辑。代码如下,但是这个清理容器的模块是运行一次就退出的(磁盘资源紧张时才运行) 所以没有这个问题。

  tr := &http.Transport{Dial: fakeDial, } 
    client := &http.Client{Transport: tr}
    resp, err := client.Get("http://localhost/containers/json")
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()

翻了下源码发现这段注释

    // Close indicates whether to close the connection after
    // replying to this request (for servers) or after sending this
    // request and reading its response (for clients).
    //
    // For server requests, the HTTP server handles this automatically
    // and this field is not needed by Handlers.
    //
    // For client requests, setting this field prevents re-use of
    // TCP connections between requests to the same hosts, as if
    // Transport.DisableKeepAlives were set.
    Close bool

http 1.1默认是保持keep-alive 的,所以如果每次请求都new 一个xin的client,就会造成fd泄漏。 按照注释,在new client时,把keep-alive 设置为disable 就可以解决这个问题。修改后代码如下

  tr := &http.Transport{Dial: fakeDial, DisableKeepAlives: true}
    client := &http.Client{Transport: tr}
    resp, err := client.Get("http://localhost/containers/json")
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()

问题1 req.Close 和transport DisableKeepAlives 如何实现 http keep-alive:close 的? 如何达到相同目的的? 问题2 使用时怎么样更科学 更安全? 长连接? 短连接?

Comments