Go net/http服务器错误:接受tcp [::]:443: accept4:打开的文件太多;正在重试

huangapple go评论84阅读模式
英文:

Go net/http server error: accept tcp [::]:443: accept4: too many open files; retrying

问题

这是我的服务器代码:

package main

import (
    "my-project/pkg/configuration"
    "my-project/pkg/logger"
    "my-project/pkg/server/appConfig"
    "my-project/pkg/server/handlers"
    "net/http"
    "os"
    "strings"
)

func main() {

    if len(os.Args) < 2 {
        logger.Log("error", "main", "Missing config.json file path as argument")
        return
    }

    configuration := configuration.Configuration{}
    appConfig.InitConfig(os.Args[1], &configuration)

    // 下载文件
    http.HandleFunc("/file-download", handlers.DownloadFile(&configuration))

    // 上传文件
    http.HandleFunc("/file-upload", handlers.UploadFile(&configuration))

    // 获取URL
    http.HandleFunc("/file-url", handlers.GetUrl(&configuration))

    // 删除
    http.HandleFunc("/delete", handlers.DeleteHandler(&configuration))

    // 文件系统
    fs := http.FileServer(http.Dir(configuration.RootStoragePath))
    corsFS := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        if strings.HasSuffix(r.URL.Path, "/") {
            http.NotFound(w, r)
            return
        }
        w.Header().Add("Access-Control-Allow-Origin", "*")
        fs.ServeHTTP(w, r)
    })

    http.Handle("/", corsFS)

    err := http.ListenAndServeTLS(":443", "crt/server.crt", "crt/server.key", nil)
    if err != nil {
        logger.Log("error", "ListenAndServeTLS", err.Error())
    }
}

服务器处于中等负载状态。
服务器在运行一天后崩溃,我得到了以下错误:

http: Accept error: accept tcp [::]:443: accept4: too many open files; retrying

命令:

ls -ltr /proc/{PROCESS_ID}/fd

文件和套接字的列表 [XXXXXX] 不断增长。
我不想更改 ulimit(1024),我不认为这是一个长期的解决方案...

我真的不知道问题可能出在哪里... 在处理程序中,我操作文件,但我确保使用 defer Close()...
我需要设置超时吗?如果需要,在哪里设置?

非常感谢您提供的帮助...

英文:

Here is my server :

package main
import (
&quot;my-project/pkg/configuration&quot;
&quot;my-project/pkg/logger&quot;
&quot;my-project/pkg/server/appConfig&quot;
&quot;my-project/pkg/server/handlers&quot;
&quot;net/http&quot;
&quot;os&quot;
&quot;strings&quot;
)
func main() {
if len(os.Args) &lt; 2 {
logger.Log(&quot;error&quot;, &quot;main&quot;, &quot;Missing config.json file path as argument&quot;)
return
}
configuration := configuration.Configuration{}
appConfig.InitConfig(os.Args[1], &amp;configuration)
// download file
http.HandleFunc(&quot;/file-download&quot;, handlers.DownloadFile(&amp;configuration))
// upload file
http.HandleFunc(&quot;/file-upload&quot;, handlers.UploadFile(&amp;configuration))
// Get url
http.HandleFunc(&quot;/file-url&quot;, handlers.GetUrl(&amp;configuration))
// Delete
http.HandleFunc(&quot;/delete&quot;, handlers.DeleteHandler(&amp;configuration))
// file system
fs := http.FileServer(http.Dir(configuration.RootStoragePath))
corsFS := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if strings.HasSuffix(r.URL.Path, &quot;/&quot;) {
http.NotFound(w, r)
return
}
w.Header().Add(&quot;Access-Control-Allow-Origin&quot;, &quot;*&quot;)
fs.ServeHTTP(w, r)
})
http.Handle(&quot;/&quot;, corsFS)
err := http.ListenAndServeTLS(&quot;:443&quot;, &quot;crt/server.crt&quot;, &quot;crt/server.key&quot;, nil)
if err != nil {
logger.Log(&quot;error&quot;, &quot;ListenAndServeTLS&quot;, err.Error())
}
}

The server is under medium load.
The server crashed after a day of running,
I got the following error:

http: Accept error: accept tcp [::]:443: accept4: too many open files; retrying

The command :

ls -ltr /proc/{PROCESS_ID}/fd

And the list of file, and socket:[XXXXXX] is growing all the time.
I don't want to change ulimit (1024), I don't think it is a long terme fix...

I don't really see where the problem could come from... In the handlers, I manipulate files but I take care to do defer Close()...
Do I have to set timeouts? If so where?

Thank you in advance for all the help ...

答案1

得分: 1

我终于成功找到了一个解决方案。

事实上,net/http 包的 http.ListenAndServe 方法默认没有超时设置。这是 Go 团队的自愿选择。因此,在生产环境中,需要声明一个 http.Server{} 并进行配置。Go 文档。来源:Cloudflare 博客文章

srv := &http.Server{
        Addr:         ":443",
        ReadTimeout:  30 * time.Second,
        WriteTimeout: 120 * time.Second,
}   
srv.SetKeepAlivesEnabled(false)

err := srv.ListenAndServeTLS("crt/server.crt", "crt/server.key")

http.DefaultServeMux 是默认的请求多路复用器,HandleFuncDefaultServeMux 中注册给定模式的处理函数。
以下是实现代码:

func main() {

        if len(os.Args) < 2 { 
                logger.Log("error", "main", "Missing config.json file path as argument")
                return
        }

        configuration := configuration.Configuration{}
        appConfig.InitConfig(os.Args[1], &configuration)

        // 下载文件
        http.HandleFunc("/file-download", handlers.DownloadFile(&configuration))

        // 上传文件
        http.HandleFunc("/file-upload", handlers.UploadFile(&configuration))

        // 获取 URL
        http.HandleFunc("/file-url", handlers.GetUrl(&configuration))

        // 删除
        http.HandleFunc("/delete", handlers.DeleteHandler(&configuration))

        // 文件系统
        fs := http.FileServer(http.Dir(configuration.RootStoragePath))
        corsFS := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
                if strings.HasSuffix(r.URL.Path, "/") {
                        http.NotFound(w, r)
                        return
                }   
                w.Header().Add("Access-Control-Allow-Origin", "*")
                fs.ServeHTTP(w, r)
        })

        http.Handle("/", corsFS)

        srv := &http.Server{
                Addr:         ":443",
                ReadTimeout:  30 * time.Second,
                WriteTimeout: 120 * time.Second,
        }   
        srv.SetKeepAlivesEnabled(false)

        err := srv.ListenAndServeTLS("crt/server.crt", "crt/server.key")
        if err != nil {
                logger.Log("error", "ListenAndServeTLS", err.Error())
                os.Exit(1)
        }
}

如果你有其他建议,我当然感兴趣。

英文:

I finally managed to find a solution.

The fact is that the http.ListenAndServe method of the net/http package has no timeout by default. This is voluntary from the Go team. So for a service in production, it is necessary to declare a http.Server{} and to configure it. Go doc.
Source : Cloudflare blog post

        srv := &amp;http.Server{
Addr:         &quot;:443&quot;,
ReadTimeout:  30 * time.Second,
WriteTimeout: 120 * time.Second,
}   
srv.SetKeepAlivesEnabled(false)
err := srv.ListenAndServeTLS(&quot;crt/server.crt&quot;, &quot;crt/server.key&quot;)

http.DefaultServeMux is the default request multiplexer, and HandleFunc registers the handler function for the given pattern in the DefaultServeMux.
Here is the implementation :

func main() {
if len(os.Args) &lt; 2 { 
logger.Log(&quot;error&quot;, &quot;main&quot;, &quot;Missing config.json file path as argument&quot;)
return
}
configuration := configuration.Configuration{}
appConfig.InitConfig(os.Args[1], &amp;configuration)
// download file
http.HandleFunc(&quot;/file-download&quot;, handlers.DownloadFile(&amp;configuration))
// upload file
http.HandleFunc(&quot;/file-upload&quot;, handlers.UploadFile(&amp;configuration))
// Get url
http.HandleFunc(&quot;/file-url&quot;, handlers.GetUrl(&amp;configuration))
// Delete
http.HandleFunc(&quot;/delete&quot;, handlers.DeleteHandler(&amp;configuration))
// file system
fs := http.FileServer(http.Dir(configuration.RootStoragePath))
corsFS := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if strings.HasSuffix(r.URL.Path, &quot;/&quot;) {
http.NotFound(w, r)
return
}   
w.Header().Add(&quot;Access-Control-Allow-Origin&quot;, &quot;*&quot;)
fs.ServeHTTP(w, r)
})
http.Handle(&quot;/&quot;, corsFS)
srv := &amp;http.Server{
Addr:         &quot;:443&quot;,
ReadTimeout:  30 * time.Second,
WriteTimeout: 120 * time.Second,
}   
srv.SetKeepAlivesEnabled(false)
err := srv.ListenAndServeTLS(&quot;crt/server.crt&quot;, &quot;crt/server.key&quot;)
if err != nil {
logger.Log(&quot;error&quot;, &quot;ListenAndServeTLS&quot;, err.Error())
os.Exit(1)
}
}

If you have any other advice, I'm obviously interested.

huangapple
  • 本文由 发表于 2022年3月10日 00:46:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/71412983.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定