必须同步调高net.ipv4.tcp_max_syn_backlog和net.core.somaxconn,二者取小值作为半连接队列上限;还需匹配应用层listen()的backlog参数,并通过SynsToListenDrops等指标验证是否真丢SYN包。

要真正提升 Linux 在大并发连接下的承载能力,关键一步是把 TCP 半连接队列(SYN Queue)调到位。它不是调一个参数就完事,而是三个层面必须对齐:内核限制、应用层配置、实际流量特征。调不对,SYN 包被静默丢弃,客户端只看到“连接超时”,排查起来特别隐蔽。
必须同步改这两个内核参数
半连接队列的实际容量,由 net.ipv4.tcp_max_syn_backlog 和 net.core.somaxconn 两者中的较小值决定。只改其中一个,基本没用。
- 临时生效:sudo sysctl -w net.ipv4.tcp_max_syn_backlog=65535 和 sudo sysctl -w net.core.somaxconn=65535
- 永久生效:把两行写进 /etc/sysctl.d/99-tcp-queue.conf,再运行 sudo sysctl --system
- 检查是否生效:sysctl net.ipv4.tcp_max_syn_backlog net.core.somaxconn
应用层 listen() 的 backlog 不能拖后腿
即使内核队列设得再大,如果应用在调用 listen() 时传的 backlog 值更小,最终还是以这个小值为准。常见服务默认值都偏保守:
- Nginx:默认 backlog=511,需显式写成
listen 80 backlog=65535; - Redis:配置项
tcp-backlog 65535 - Go 程序:需在
net.ListenConfig.Control回调里用setsockopt设置 SO_BACKLOG - Java(Tomcat):在
server.xml的 Connector 中配acceptCount="65535"
验证方式:ss -lnt | grep :端口,看 Recv-Q 是否接近你设的目标值。
确认到底有没有丢 SYN 包
别只盯着 netstat -s | grep "listen overflows",那是全连接队列溢出。半连接队列丢包要看更底层指标:
- 执行:awk '/^TcpExt/ {print $1,$2}' /proc/net/snmp | grep SynsToListen
- 重点观察 SynsToListenDrops 是否持续增长 → 表明 SYN 正被内核丢弃
- 辅助验证:ss -n state syn-recv | wc -l,对比当前
tcp_max_syn_backlog值 - 抓包确认:tcpdump -i any 'tcp[tcpflags] & tcp-syn != 0 and dst port 80',看是否有大量 SYN 进来但无 SYN+ACK 返回
配合其他策略稳住局面
扩容队列是基础,但面对真实高并发或攻击场景,还需叠加控制手段:
- 开启 SYN Cookie:sudo sysctl -w net.ipv4.tcp_syncookies=1,队列满时自动兜底(注意不支持 SACK)
- 缩短重试时间:sudo sysctl -w net.ipv4.tcp_synack_retries=2,避免无效连接长期占位
- 容器部署时别漏掉透传:docker run --sysctl net.core.somaxconn=65535 ...
- 前端加反向代理并启用 keepalive,把短连接变成长连接,从源头降低 SYN 到达速率


















