同事有个模块会定期获取宿主机上的所有容器信息,代码中使用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 使用时怎么样更科学 更安全? 长连接? 短连接?