对于在 Caddy 后面运行的 Golang 服务器来说,打开的文件过多

对于在 Caddy 后面运行的 Golang 服务器来说,打开的文件过多

我正在运行一个 Go 二进制文件,它有一个带 systemd 的 http 服务器。我已将其设置为 Caddy 可以反向代理此 go http 服务器。

http: Accept error: accept tcp [::]:8002: accept4: too many open files;
dial tcp 192.85.2.4:443: socket: too many open files

当我查看该进程的打开文件数时,得到的结果为 1025 或更少,尽管我的 ulimit 设置得更大:

$ lsof -p 1243 | wc -l
1025
$ ulimit -Sn
200000
$ ulimit -Hn
1048576

我不确定这是否是问题所在,但似乎可能是这样?似乎 Go 服务器应该生成新的 goroutine 或以某种方式处理这个问题。

编辑:这是我的服务器脚本:

package main
  
import (
        "fmt"
        "time"
        "net/http"
)

type Config struct{}


func (c *Config) testerHandler(w http.ResponseWriter, r *http.Request) {
    r.Body.Close()
    time.Sleep(1 * time.Second)
    fmt.Fprintf(w, "hello\n")
}

func main() {
        c := &Config{}
        http.HandleFunc("/tester", c.testerHandler)
        fmt.Println("listening on http://127.0.0.1:8000")
        http.ListenAndServe(":8000", nil)
}

编辑:这是我用来向服务器发送垃圾邮件的脚本:

package main
  
import (
        "log"
        "net/http"
)

func main() {
        number := 10000
        log.Printf("spamming %d numbers\n", number)
        ch := make(chan interface{})
        client := http.Client{}

        for i:=0; i<number; i++{
                go func(number int) {
                        u := "http://127.0.0.1:8000/tester"
                        rsp, err := client.Get(u)
                        if err != nil {
                                ch <- err
                                return
                        }
                        rsp.Body.Close()

                        ch <- rsp
                }(number)
        }

        var errs int
        m := make(map[int]int)

        for i:=0; i<number; i++ {
                rsp := <-ch
                switch rsp.(type) {
                case *http.Response:
                        code := rsp.(*http.Response).StatusCode
                        m[code]++
                default:
                        log.Println(rsp.(error))
                        errs++
                }
        }

        log.Println("errs:", errs)
        for k, v := range m {
                log.Printf("%d:%d\n", k, v)
        }
}

答案1

这个问题可以通过修改系统的某些参数来解决。

我建议您关注以下主题:打开的文件过多

答案2

我可以通过在 ReverseProxy 上禁用 Keep Alives 或在 Go Server 上设置较低的“ReadTimeout”来解决此问题。

简单示例

func main(){
srv := &http.Server{
    Addr: ":8080",
    ReadTimeout:    2 * time.Second,
    Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        go fmt.Printf("handling request\n")
        fmt.Fprintln(w, "Hello, client")
    }),
}
log.Fatal(srv.ListenAndServe())
}

答案3

Systemd 接管 ulimits 并将实际(=软)运行时最大文件数限制设置为 1024。预计该进程会负责明确请求所需的资源 - 大多数“专业”应用程序都是这样编写的。对于自制应用程序,可以在 dot-service 文件中请求资源,例如:

[Service]
LimitNOFILE=10240
LimitNOFILESoft=10240

相关内容