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

Here is my server :

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)

        // download file
        http.HandleFunc("/file-download", handlers.DownloadFile(&configuration))

        // upload file
        http.HandleFunc("/file-upload", handlers.UploadFile(&configuration))

        // Get url
        http.HandleFunc("/file-url", handlers.GetUrl(&configuration))

        // Delete
        http.HandleFunc("/delete", handlers.DeleteHandler(&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, "/") {
                        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())
        }
}

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 ...



Solution 1:[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 := &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 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) < 2 { 
                logger.Log("error", "main", "Missing config.json file path as argument")
                return
        }

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

        // download file
        http.HandleFunc("/file-download", handlers.DownloadFile(&configuration))

        // upload file
        http.HandleFunc("/file-upload", handlers.UploadFile(&configuration))

        // Get url
        http.HandleFunc("/file-url", handlers.GetUrl(&configuration))

        // Delete
        http.HandleFunc("/delete", handlers.DeleteHandler(&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, "/") {
                        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)
        }
}

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

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 pksimba