Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/go/7.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Go net/http服务器:打开的文件太多错误_Go - Fatal编程技术网

Go net/http服务器:打开的文件太多错误

Go net/http服务器:打开的文件太多错误,go,Go,我正试图开发一个简单的作业队列服务器,其中有一些工作人员查询它,但我的net/http服务器遇到了一个问题。我确实做了一些不好的事情,但大约3分钟后,我的服务器开始显示: http:Accept错误:Accept tcp[::]:4200:Accept 4:打开的文件太多;在1秒内重试 在我的测试用例中,它每秒接收10个请求 这里有两个文件可以重现此错误: // server.go package main import ( "net/http" ) func main() {

我正试图开发一个简单的作业队列服务器,其中有一些工作人员查询它,但我的net/http服务器遇到了一个问题。我确实做了一些不好的事情,但大约3分钟后,我的服务器开始显示:

http:Accept错误:Accept tcp[::]:4200:Accept 4:打开的文件太多;在1秒内重试

在我的测试用例中,它每秒接收10个请求

这里有两个文件可以重现此错误:

// server.go
package main

import (
    "net/http"
)

func main() {
    http.HandleFunc("/get", func(rw http.ResponseWriter, r *http.Request) {
        http.Error(rw, "Try again", http.StatusInternalServerError)
    })
    http.ListenAndServe(":4200", nil)
}

// worker.go
package main

import (
    "net/http"
    "time"
)

func main() {
    for {
        res, _ := http.Get("http://localhost:4200/get")
        defer res.Body.Close()

        if res.StatusCode == http.StatusInternalServerError {
            time.Sleep(100 * time.Millisecond)
            continue
        }

        return
    }
}
我已经对这个错误做了一些搜索,并找到了一些有趣的响应,但没有一个解决了我的问题

我看到的第一个响应是正确地关闭http.Get响应中的主体,正如您所看到的那样

第二个响应是更改我的系统的文件描述符ulimit,但由于我无法控制我的应用程序的运行位置,因此我宁愿不使用此解决方案(但请注意,在我的系统上它设置为1024)

有人能解释一下为什么会出现这个问题,以及我如何在代码中修复它吗

非常感谢你抽出时间

编辑:


编辑2:在评论中,马丁说我没有关闭身体,我试图关闭它(没有延迟),它解决了这个问题。谢谢你,马丁!我认为continue将执行我的延迟,我错了。

正如Martin在评论中所说的,在Get请求之后,我并没有真正关闭身体。我使用了
defer res.Body.Close()
,但由于我处于for循环中,所以没有执行它。因此,请注意,在某些情况下,/etc/sysctl.conf中的设置 net.ipv4.tcp_tw_recycle=1

可能会导致此错误,因为TCP连接保持打开状态。

我找到了一个更详细的方法来解释根本问题。 Nathan Smith甚至解释了如何在TCP级别控制超时(如果需要)。 下面是我能找到的关于这个特殊问题的所有内容的总结,以及在将来避免这个问题的最佳实践

问题 当接收到响应时,无论是否需要响应主体,连接都保持活动状态,直到响应主体流关闭。因此,正如本线程中提到的,始终关闭响应主体。即使您不需要使用/阅读正文内容:

func Ping(url string) (bool) {
    // simple GET request on given URL
    res, err := http.Get(url)
    if err != nil {
        // if unable to GET given URL, then ping must fail
        return false
    }

    // always close the response-body, even if content is not required
    defer res.Body.Close()

    // is the page status okay?
    return res.StatusCode == http.StatusOK
}
最佳做法 正如Nathan Smith所提到的,在生产系统中从不使用
http.DefaultClient
,这包括像
http.Get
这样的调用,因为它在基础上使用
http.DefaultClient

避免使用
http.DefaultClient
的另一个原因是它是一个单例(包级别变量),这意味着垃圾收集器不会尝试清理它,这将使空闲的后续流/套接字保持活动状态

而是创建您自己的
http.Client
实例,并记住始终指定一个正常的
超时

func Ping(url string) (bool) {
    // create a new instance of http client struct, with a timeout of 2sec
    client := http.Client{ Timeout: time.Second * 2 }

    // simple GET request on given URL
    res, err := client.Get(url)
    if err != nil {
        // if unable to GET given URL, then ping must fail
        return false
    }

    // always close the response-body, even if content is not required
    defer res.Body.Close()

    // is the page status okay?
    return res.StatusCode == http.StatusOK
}
安全网 安全网是为团队中的新手准备的,他们不知道
http.DefaultClient
用法的不足之处。甚至是非常有用但不太活跃的开源库,仍然充斥着
http.DefaultClient
调用

由于
http.DefaultClient
是一个单例,我们可以轻松更改
超时设置,以确保遗留代码不会导致空闲连接保持打开状态

我发现最好在
init
函数中的
package main
文件中设置:

package main

import (
    "net/http"
    "time"
)

func init() {
    /*
    Safety net for 'too many open files' issue on legacy code.
    Set a sane timeout duration for the http.DefaultClient, to ensure idle connections are terminated.
    Reference: https://stackoverflow.com/questions/37454236/net-http-server-too-many-open-files-error
    */
    http.DefaultClient.Timeout = time.Minute * 10
}

临时解决方案,只需增加打开文件的数量:

ulimit -Sn 10000

如果您在linux上,请使用
ps aux | grep{program name}
获取进程ID,然后使用
ls-l/proc/{process ID}/fd
查看发生此错误时打开的文件。将输出添加到您的问题中。谢谢这会有帮助我相信你无论如何都应该更新系统属性。试试这个:
echo fs.inotify.max_user_watches=524288 | sudo tee-a/etc/sysctl.conf&&sudo sysctl-p
您的defer res.Body.Close()正在排队等待所有资源释放,直到worker main()返回。检查
http后立即显式调用
res.Body.Close()
。获取
错误并查看其性能是否更好。是的,Martin是正确的:您从未关闭任何请求。Body.Default timeout为10分钟可以吗?也许10秒钟?