去,tcp太多打开文件调试

时间:2015-09-01 06:55:29

标签: 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()
}

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

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

其他帖子说确保Close连接,我在这里做的就是这一切。 其他人说要增加与ulimitsudo sysctl -w fs.inotify.max_user_watches=100000的最大连接限制,但仍然无效。

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

谢谢,

7 个答案:

答案 0 :(得分:23)

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

FWIW,您的程序在我的VM上运行良好。

·> ulimit -n
120000

但是在我跑完

之后
·> ulimit -n 500
·> ulimit -n
500

我明白了:

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

答案 1 :(得分:4)

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

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()
}

答案 2 :(得分:1)

也可以在你的函数中使用goruntine,试试这个https://github.com/leenanxi/nasync

 //it has a simple usage
 nasync.Do(yourAsyncTask)

代码

for i := 0; i < 2000; i++ {
    nasync.Do(func() {
        resp, err := http.Get("https://www.baidu.com")
        ...
    })
}

nasync lib中的默认max go goruntine为1000

答案 3 :(得分:1)

Go的http软件包默认情况下未指定请求超时。您应该始终在服务中包括超时。如果客户不关闭会话怎么办?您的过程将使旧的会话保持活跃。一个坏演员可能会故意打开数千个会话,以使您的服务器处于DOS状态。重载服务也应调整ulimit,但应为backstop设置超时。

确保您指定了超时时间:

http.DefaultClient.Timeout = time.Minute * 10

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

lsof -p [PID_ID]

答案 4 :(得分:0)

更改ulimit以避免错误“打开文件太多” 默认情况下,max ulimit对于linux为4096,对于mac为1024, 您可以通过键入将ulimit更改为4096 ulimit -n 4096 超过4096你需要在linux的etc / security文件夹中修改limits.conf并通过添加这行“* hard core 100000”将硬限制设置为100000

答案 5 :(得分:0)

HTTP/1.1 uses persistent connections by default:
A significant difference between HTTP/1.1 and earlier versions of HTTP is that persistent connections are the default behavior of any HTTP connection.
http://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html
The 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,
req.Header.Set("Connection", "close") or by setting the Close property to true on the http.Request:
req.Close = true After doing that, the “too many open files” issue went away as the program was no longer keeping HTTP connections open and thus not using up file descriptors.

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

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

答案 6 :(得分:-1)

我还必须手动设置关闭连接头以避免文件描述符问题:

r, _ := http.NewRequest(http.MethodDelete, url, nil)
r.Close = true
res, err := c.Do(r)
res.Body.Close();

没有r.Close = true和res.Body.Close()我达到文件描述符限制。我可以随心所欲地开火。