Go程序单机支撑几十万长连接需同时调整Linux文件描述符三层限制:进程软硬限、systemd服务LimitNOFILE、系统级fs.file-max,缺一不可。
go 程序在单机跑几十万长连接时,真正卡住你的不是 net/http 或 goroutine,而是 linux 内核对文件描述符(fd)的三层限制:进程软硬限、systemd 服务限、系统全局限。只改其中一层,too many open files 仍会准时报错。
为什么 ulimit -n 改了没用?
常见现象是终端里执行 ulimit -n 65535 后,go run main.go 一压测就崩。原因很实在:
-
ulimit -n只影响当前 shell 及其直接子进程;systemd 启动的服务完全不读它 - 软限制(soft)不能超过硬限制(hard),而
ulimit -Hn默认常是 4096,你没调 hard,soft 就静默卡在 4096 - Go 进程启动后实际能用多少 FD,得看
/proc/<pid>/limits里的Max open files,不是你终端里ulimit -n的输出
systemd 服务必须配 LimitNOFILE
生产环境几乎全走 systemd,不配就永远卡在 1024 或 4096。操作很简单:
- 编辑服务单元文件,比如
/etc/systemd/system/myapp.service - 在
[Service]段下加:LimitNOFILE=65536(同时设 soft 和 hard);若需区分,写成LimitNOFILE=65536:65536 - 执行:
sudo systemctl daemon-reload && sudo systemctl restart myapp - 验证:
cat /proc/$(pidof myapp)/limits | grep "Max open files",确认两列值都 ≥ 65536
Go 程序内必须调 syscall.Setrlimit
即使 systemd 配好了,容器内、supervisord 启动、或某些 init 系统下的 Go 进程仍可能拿不到预期值。稳妥做法是在 main() 开头主动申请:
Zynq‑7000 / Zynq UltraScale+ 硬件调试技能。适用于 Xilinx Zynq(Cortex‑A9/A53 + FPGA)Linux 环境,调试 I2C、I2S、UART、GPIO 等外设。包括设备树配置、内核驱动、用户空间调试工具、交叉编译和常见问题排查,支持 PetaLinux 开发。
package main
import (
"fmt"
"syscall"
)
func main() {
err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, &syscall.Rlimit{
Cur: 65535,
Max: 65535,
})
if err != nil {
fmt.Println("Error setting rlimit:", err)
return
}
// 后续逻辑...
}
注意:Cur 不能大于 Max,且该调用必须在任何 goroutine 创建前执行(尤其不能晚于 http.ListenAndServe)。
别漏掉系统级总限制 fs.file-max
所有进程 FD 总和不能超过内核参数 fs.file-max。如果目标是百万连接,这个值至少要设到 2097152:
- 临时生效:
sudo sysctl -w fs.file-max=2097152 - 永久生效:往
/etc/sysctl.conf加一行fs.file-max = 2097152,再执行sudo sysctl -p - 验证:
cat /proc/sys/fs/file-max
最容易被忽略的是:即使 Go 程序和 systemd 都调到了 65536,但 fs.file-max 只有 10 万,系统级资源池就满了,新连接照样被拒——这时 lsof -p <pid> | wc -l 看单个进程没满,但 cat /proc/sys/fs/file-nr 会显示已用接近上限。

















