【发布时间】:2016-05-26 07:28:18
【问题描述】:
我正在尝试使用一些查询它的工作人员开发一个简单的作业队列服务器,但我的 net/http 服务器遇到了问题。我肯定做错了什么,但大约 3 分钟后我的服务器开始显示:
http: Accept error: accept tcp [::]:4200: accept4: too many open files; 1s 后重试
在我的测试用例中,它每秒收到 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 响应中的 Body,如您所见,我做到了。
第二个响应是更改我的系统的文件描述符 ulimit,但由于我无法控制我的应用程序将在哪里运行,我宁愿不使用此解决方案(但对于信息,它在我的系统上设置为 1024)
谁能解释我为什么会出现这个问题以及如何在我的代码中解决它?
非常感谢您的宝贵时间
编辑 2:Martin 在评论中说我没有关闭 Body,我试图关闭它(没有延迟),它解决了这个问题。谢谢马丁!我以为 continue 会执行我的 defer,我错了。
【问题讨论】:
-
如果您使用的是 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.Get错误后立即显式调用res.Body.Close(),看看它是否表现更好。 -
是的,Martin 是正确的:你从不关闭任何 Request.Body。
标签: go