服务器TCP队列溢出,net.core.somaxconn
服务器 TCP 队列溢出时,最典型的表现就是客户端连接缓慢、请求超时,而服务器 CPU 和内存看起来并不高。
这个问题多半出在 net.core.somaxconn 和应用层 backlog 参数配合不当上。
本文会从现象确认开始,一步步带你完成调参和验证,即使你不熟悉 Linux 内核也能照做。
先判断是不是 TCP 队列溢出
不要一上来就改参数,先确认问题确实存在。
登录服务器后执行:
ss -lnt
重点看 Send-Q 列。
当 Send-Q 显示的值接近或等于当前监听 socket 的 backlog 上限,同时客户端出现 connect 超时,说明队列很可能已经挤满。
再用下面命令查看系统当前允许的最大 backlog:
sysctl net.core.somaxconn
如果输出是 128 或 256,而你的业务并发连接数经常超过这个值,那么队列溢出几乎不可避免。
调整 net.core.somaxconn 系统参数
net.core.somaxconn 是 Linux 内核层面的 socket 监听队列上限,所有应用默认不能超过这个值。
调大它的方法如下。
编辑 /etc/sysctl.conf:
sudo nano /etc/sysctl.conf
在文件末尾追加:
net.core.somaxconn = 1024
保存后执行:
sudo sysctl -p
验证是否生效:
sysctl net.core.somaxconn
如果输出 net.core.somaxconn = 1024,内核参数就调好了。
同步调整应用层 backlog
很多人只调了内核参数,但应用自己没改,结果还是溢出。
因为应用创建监听 socket 时会传入自己的 backlog,如果它比 somaxconn 小,实际生效的还是小的那个。
以 Nginx 为例,在 nginx.conf 的 events 块中增加:
events {
worker_connections 4096;
}
然后在 server 或 listen 指令中指定 backlog:
listen 80 backlog=1024;
改完执行:
nginx -t
nginx -s reload
如果是 Redis,在 redis.conf 中调整:
tcp-backlog 1024
然后重启 Redis 服务。
关键点:应用本身的 backlog 一定要小于或等于 net.core.somaxconn,通常两者设为相同值即可。
容易踩的三个坑
1. 只改内核不改应用
这是最常见的无效操作。
内核 somaxconn 只决定上限,应用实际请求的 backlog 可能只有 511 或更低。
改完后用 ss -lnt 看 Send-Q,如果还是原来的值,说明应用没生效。
2. 调得过大反而增加延迟
backlog 不是越大越好。
队列太长会让请求在排队中等待更久,客户端更容易超时。
建议先从 1024 开始,观察峰值连接数和响应时间再决定是否继续加大。
3. 忽略了 tcp_max_syn_backlog
如果服务器经常收到大量半连接请求,还需要检查 net.ipv4.tcp_max_syn_backlog,它控制 SYN 队列长度。
可以一并调整:
net.ipv4.tcp_max_syn_backlog = 2048
同样加入 /etc/sysctl.conf 后执行 sysctl -p。
验证调整是否真正生效
调整完先重启应用,再执行:
ss -lnt | grep :80
看 Send-Q 是否变成了你设置的 backlog 值。
比如你给 Nginx 设置了 backlog=1024,这里应显示:
State Recv-Q Send-Q Local Address:Port
LISTEN 0 1024 0.0.0.0:80
接着用压测工具模拟并发连接:
ab -n 5000 -c 500 http://你的服务器IP/
观察 Failed requests 是否明显下降,同时用 ss -s 查看当前 socket 使用情况。
如果 Send-Q 不再顶满,连接不再超时,说明 TCP 队列溢出的问题已经解决。
最后提醒一句:不同操作系统、不同应用版本对 backlog 的默认值不同,调参时建议以实际压测结果为准,不要照搬别人的数值。
如果你的业务连接数还在增长,回头再检查一下这两个参数,并配合监控工具持续观察队列长度。