我正在运行一个 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