go默认listen backlog旧版(1.11前)固定为128,新版(1.12+)启动时读取/proc/sys/net/core/somaxconn值且仅读一次,不感知运行时变更;若内核未调优或go未重启,实际生效值仍为128,导致全连接队列溢出引发connection refused。

Go 服务在高并发下报 Connection refused,大概率是 net.core.somaxconn 和 Go 的 listen backlog 没对齐
Go 默认 listen backlog 是多少?
旧版 Go(1.11 之前)net.Listen("tcp", ":80") 固定传 128 给内核的 listen() 系统调用;新版 Go(1.12+)会读取 /proc/sys/net/core/somaxconn 当前值作为默认 backlog,但仅限启动时读一次 —— 如果你之后改了 net.core.somaxconn,Go 进程不会自动感知。
也就是说:
- 若你没动过内核参数,net.core.somaxconn = 128,Go 启动后 backlog 就是 128;
- 若你临时调大到 65535,但 Go 已经跑起来了,它仍用 128;
- 即使你永久配置并 sysctl -p,Go 也不会 reload 这个值。
常见错误现象:
- ss -ltn 看到监听端口的 Send-Q 是 128,不是你期望的 65535;
- 压测时连接成功率骤降,Connection refused 出现,但 CPU、内存、网络带宽都空闲。
- 验证方式:
ss -ltn | grep :80,看第三列(Send-Q)是否等于你设的net.core.somaxconn - Go 程序必须重启才能生效新 backlog,热更新无效
- 别依赖 “新版 Go 自动读取”,它只读一次,且不处理运行时变更
如何让 Go 显式控制 backlog?
标准库不暴露 backlog 参数,但可通过 net.ListenConfig + 自定义 Control 函数绕过:
Zynq‑7000 / Zynq UltraScale+ 硬件调试技能。适用于 Xilinx Zynq(Cortex‑A9/A53 + FPGA)Linux 环境,调试 I2C、I2S、UART、GPIO 等外设。包括设备树配置、内核驱动、用户空间调试工具、交叉编译和常见问题排查,支持 PetaLinux 开发。
lc := net.ListenConfig{
Control: func(fd uintptr) {
syscall.SetsockoptInt32(int(fd), syscall.SOL_SOCKET, syscall.SO_BACKLOG, 65535)
},
}
ln, err := lc.Listen(context.Background(), "tcp", ":8080")
注意:
- 必须在 Listen 调用前设置,不能对已建立的 listener 生效;
- SO_BACKLOG 在部分旧内核或容器环境可能被忽略,最终仍受限于 net.core.somaxconn;
- 更稳妥的做法是:先确保 net.core.somaxconn ≥ 65535,再用上述方式显式设值,双重保险。
- 若用
http.Server.Serve(ln),ln必须是上面构造的,不能用http.ListenAndServe简写 - 第三方包如
github.com/valyala/fasthttp默认就设了较大 backlog,可直接用 - Docker 容器里要确认宿主机和容器 namespace 都调大了
net.core.somaxconn,否则容器内读不到新值
为什么 ss -ltn 的 Send-Q 不是 65535?
Send-Q 显示的是内核实际为该 socket 分配的全连接队列长度,它取的是 min(backlog 参数, net.core.somaxconn)。哪怕你在 Go 里写了 65535,只要 net.core.somaxconn 还是 128,Send-Q 就是 128。
典型陷阱:
- 你改了 /etc/sysctl.conf 并 sysctl -p,但忘了检查是否生效:sysctl net.core.somaxconn;
- 你用了 systemd 启动 Go 服务,而 systemd 默认限制 LimitNOFILE 和 LimitNPROC,导致即使 backlog 够大,也卡在 fd 限制上;
- Kubernetes Pod 中未通过 securityContext.sysctls 显式设置 net.core.somaxconn,Pod 内看到的仍是节点默认值。
- 查真实值:
cat /proc/sys/net/core/somaxconn,不是只信配置文件 - 查 Go 进程是否生效:
ss -ltn | grep :port,Send-Q 必须和前者一致 - 查 fd 限制:
cat /proc/$(pgrep your-go-app)/limits | grep "Max open files"
真正起作用的是内核值和 Go 启动时读到的值共同决定的最小值,不是其中某一个单独说了算。最容易被忽略的是:Go 进程不 reload 内核参数,也不报错,它就默默用旧值撑着,直到你看见 Connection refused 才发现不对劲。










