net/http 传输超出 MaxConnsPerHost

sse*_*lla 6 go

我有一个简单的程序,下面是一个简单的 HTTP 客户端和服务器。我正在测试Go 1.11MaxConnsPerHosthttp.Transport引入的功能是否如宣传的那样工作。但是,当我运行代码超过 10-30 分钟时,ESTABLISHED 连接慢慢超过了设置。难道我做错了什么?MaxConnsPerHost

package main

import (
    "io/ioutil"
    "log"
    "net"
    "net/http"
    "time"
)

func main() {

    // Server
    //
    go func() {

        if err := http.ListenAndServe(":8081", http.HandlerFunc(
            func(w http.ResponseWriter, r *http.Request) {
                w.Write([]byte("hi"))
            },
        )); err != nil {
            log.Fatal(err)
        }

    }()

    // Client settings
    //
    c := &http.Client{
        Timeout: 30 * time.Second,
        Transport: &http.Transport{
            Proxy: http.ProxyFromEnvironment,
            DialContext: (&net.Dialer{
                Timeout:   30 * time.Second,
                KeepAlive: 30 * time.Second,
                DualStack: true,
            }).DialContext,
            MaxIdleConns:          100,
            IdleConnTimeout:       90 * time.Second,
            TLSHandshakeTimeout:   10 * time.Second,
            ExpectContinueTimeout: 1 * time.Second,
            MaxConnsPerHost:       50,
        },
    }

    // Worker loop
    //
    threads := 500
    for i := 0; i < threads; i++ {

        go func() {

            for {

                req, err := http.NewRequest("GET", "http://localhost:8081", nil)
                if err != nil {
                    log.Fatal(err)
                }

                res, err := c.Do(req)
                if err != nil {
                    log.Fatal(err)
                }

                if _, err := ioutil.ReadAll(res.Body); err != nil {
                    log.Fatal(err)
                }

                res.Body.Close()
            }

        }()
    }

    var done chan bool
    <-done
    log.Println("Done")

}
Run Code Online (Sandbox Code Playgroud)

运行了很长时间后,报告的ESTABLISHED连接netstat已经超过50个。

PS 我们的一项服务存在问题,即使我们正确关闭响应的正文,该服务也会严重泄漏已建立的连接。它目前使用Go 1.10构建,我希望Go 1.11 MaxConnsPerHost能成为一个解决方案,但它似乎在重负载下也会崩溃。

Яро*_*лин 1

难道我做错了什么?

看来这个错误已经在更新的 go 版本中得到解决,我无法用以下命令重现这个问题:

$ go version 
go version go1.17.1 linux/amd64
Run Code Online (Sandbox Code Playgroud)

监控:

watch -n 3 'ss  -tn | grep :8081 | sort -k 3,3n | tee >(wc -l) | tail ;
            ps -eo pid,etime,cmd | grep "leaky[-]server"'
Run Code Online (Sandbox Code Playgroud)

运行服务器:

go run leaky-server.go
Run Code Online (Sandbox Code Playgroud)

2.5小时后的样本输出:

Every 3.0s: ss  -tn | grep :8081 | sor...  balmora: Sun Oct 31 12:38:52 2021

ESTAB 0      118                 [::1]:8081               [::1]:43814
ESTAB 0      118                 [::1]:8081               [::1]:43818
ESTAB 0      118                 [::1]:8081               [::1]:43822
ESTAB 0      118                 [::1]:8081               [::1]:43824
ESTAB 0      118                 [::1]:8081               [::1]:43830
ESTAB 0      118                 [::1]:8081               [::1]:43834
ESTAB 0      118                 [::1]:8081               [::1]:43836
ESTAB 0      118                 [::1]:8081               [::1]:43838
ESTAB 0      118                 [::1]:8081               [::1]:43844
100
 536182    02:30:03 go run leaky-server.go
 536269    02:30:03 /tmp/go-build2453595122/b001/exe/leaky-server
Run Code Online (Sandbox Code Playgroud)