去,tcp太多打开文件调试

14 sockets tcp go goroutine

这是一个简单的Go http(tcp)连接测试脚本

func main() {
    ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintln(w, "Hello, client")
    }))
    defer ts.Close()
    var wg sync.WaitGroup
    for i := 0; i < 2000; i++ {
        wg.Add(1)
        go func(i int) {
            defer wg.Done()
            resp, err := http.Get(ts.URL)
            if err != nil {
                panic(err)
            }
            greeting, err := ioutil.ReadAll(resp.Body)
            resp.Body.Close()
            if err != nil {
                panic(err)
            }
            fmt.Printf("%s", i, greeting)
        }(i)
    }
    wg.Wait()
}
Run Code Online (Sandbox Code Playgroud)

如果我在Ubuntu中运行它,我得到:

panic: Get http://127.0.0.1:33202: dial tcp 127.0.0.1:33202: too many open files

其他帖子说要确保Close连接,我在这里做这一切.其他人说要增加最大连接的限制ulimit或尝试sudo sysctl -w fs.inotify.max_user_watches=100000但仍然不起作用.

如何在单个服务器中运行数百万个tcp连接goroutine?它仅与2,000个连接崩溃.

谢谢,

sbe*_*rry 28

我认为你需要改变你的最大文件描述符.我之前在我的一个开发虚拟机上遇到了同样的问题,需要更改文件描述符max,而不是任何带有inotify设置的东西.

FWIW,您的程序在我的VM上运行正常.

·> ulimit -n
120000
Run Code Online (Sandbox Code Playgroud)

但是在我跑完之后

·> ulimit -n 500
·> ulimit -n
500
Run Code Online (Sandbox Code Playgroud)

我明白了:

panic: Get http://127.0.0.1:51227: dial tcp 127.0.0.1:51227: socket: too many open files
Run Code Online (Sandbox Code Playgroud)

  • 如果增加ulimit,最终文件描述符仍然用完。尝试使用缓冲通道并将打开文件的go例程的数量限制为ulimit以下。一次打开1000个文件应该足够了... (4认同)

Pod*_*.io 13

Go 的 http 包默认不指定请求超时。您应该始终在服务中包含超时。如果客户不关闭他们的会话怎么办?您的进程将保持活跃的旧会话达到 ulimits。一个坏演员可能会故意打开数千个会话,对您的服务器进行 DOS 攻击。重负载服务也应该调整 ulimits,但会调整 backstop 的超时时间。

确保您指定超时:

http.DefaultClient.Timeout = time.Minute * 10
Run Code Online (Sandbox Code Playgroud)

您可以通过监控进程打开的文件前后验证:

lsof -p [PID_ID]
Run Code Online (Sandbox Code Playgroud)

  • 太好了,这正是我正在寻求的实践智慧。 (2认同)

小智 9

HTTP/1.1 uses persistent connections by default:\nA significant difference between HTTP/1.1 and earlier versions of HTTP is that persistent connections are the default behavior of any HTTP connection.\nhttp://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html\nThe solution was to inform the server that the client wants to close the connection after the transaction is complete. This can be done by setting the Connection header,\nreq.Header.Set("Connection", "close") or by setting the Close property to true on the http.Request:\nreq.Close = true After doing that, the \xe2\x80\x9ctoo many open files\xe2\x80\x9d issue went away as the program was no longer keeping HTTP connections open and thus not using up file descriptors.\n
Run Code Online (Sandbox Code Playgroud)\n\n

我通过添加 req.Close = true 和 req.Header.Set("Connection", "close") 解决了这个问题。我认为这比更改 ulimit 更好。

\n\n

来源:http ://craigwickesser.com/2015/01/golang-http-to-many-open-files/

\n


Pop*_*dic 5

如果您想运行数以百万计的打开/读取/关闭套接字的go例程,那么最好提高您的ulimit或打开/读取/关闭套接字并将read的值传递给go例程,但是我会使用缓冲的通道来控制您希望能够打开多少个文件描述符。

const (
    // this is where you can specify how many maxFileDescriptors
    // you want to allow open
    maxFileDescriptors = 100
)

func main() {
    ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintln(w, "Hello, client")
    }))
    defer ts.Close()
    var wg sync.WaitGroup
    maxChan := make(chan bool, maxFileDescriptors)
    for i := 0; i < 1000; i++ {
        maxChan <- true
        go func(url string, i int, maxChan chan bool, wg *sync.WaitGroup) {
            wg.Add(1)
            defer wg.Done()
            defer func(maxChan chan bool) { <-maxChan }(maxChan)
            resp, err := http.Get(url)
            if err != nil {
                panic(err)
            }
            greeting, err := ioutil.ReadAll(resp.Body)
            if err != nil {
                panic(err)
            }
            err = resp.Body.Close()
            if err != nil {
                panic(err)
            }
            fmt.Printf("%d: %s", i, string(greeting))
        }(ts.URL, i, maxChan, &wg)
    }
    wg.Wait()
}
Run Code Online (Sandbox Code Playgroud)

  • 我相信这有一个(非常不可能但可能)竞争条件错误 - 你应该在“go func(...) { ... }”之外和之前使用“wg.Add(1)”。否则,如果第一个“n”协程在第“n + 1”个协程开始之前完成(这也是极不可能的),那么“wg.Wait()”命令实际上可能在“n+1”之前终止th 和剩余的 goroutine 开始运行。(它们仍然会运行,但下游代码不会等待它。) (2认同)