so_backlog控制全连接队列(accept queue)长度,即已完成三次握手但尚未被accept()取走的连接数;它不处理syn(由tcp_max_syn_backlog管),队列满时内核丢弃ack导致客户端超时或connection refused,实际生效值取应用设置与somaxconn的较小者。

SO_BACKLOG 参数不控制三次握手本身,而是决定“已完成三次握手、但还没被应用调用 accept() 取走”的连接能缓存多少个——也就是全连接队列(Accept Queue)的长度。它对高并发建连的影响,关键在于是否匹配实际连接到达速率与应用处理能力之间的差值。
SO_BACKLOG 实际管什么
很多人误以为它控制“等待建立连接”的总数,其实不是:
- 第一次握手(SYN)进来后,连接进的是半连接队列(SYN Queue),大小由内核参数 net.ipv4.tcp_max_syn_backlog 决定,和 SO_BACKLOG 无关;
- 第三次握手(ACK)完成,连接才从 SYN Queue 移到全连接队列(Accept Queue),这时 SO_BACKLOG 才起作用;
- 这个队列满了,内核默认丢弃新完成的 ACK(不重发 SYN-ACK),客户端会超时重试,最终报 connection timeout 或 connection refused。
为什么默认值在高并发下容易出问题
Netty 4.1+ 默认 SO_BACKLOG = 128,Linux 系统默认 net.core.somaxconn = 128,两者取小值,实际队列最多就 128 个连接:
- 单线程 accept 一次只取一个连接,若每秒建连 500 次,而业务逻辑处理慢(比如鉴权、初始化耗时),队列几秒就溢出;
- netstat -s | grep "listen overflows" 计数上涨,就是明确信号;
- 现象是:压测时部分请求连不上,日志里出现 TCP: possible SYN flooding(其实是 accept 队列满,被误判为攻击)。
怎么设才合理
不是越大越好,要结合架构和系统能力协同调优:
- 应用层设置 SO_BACKLOG ≥ 1024(推荐 2048 或 4096),例如:
b.option(ChannelOption.SO_BACKLOG, 4096); - 同步调大系统上限:
sysctl -w net.core.somaxconn=65535,否则你设 4096 也没用,内核仍按 128 截断; - 如果用单进程监听,必须保证 event loop 中 accept 是循环调用(直到返回 EAGAIN),不能一次只 accept 一次;
- 更稳妥的做法是配合 SO_REUSEPORT 启动多个进程/线程监听同一端口,把连接分散到多个 accept 队列,避免单点瓶颈。
配套必须检查的内核参数
只改 SO_BACKLOG 不够,这些参数也要对齐:
- net.ipv4.tcp_max_syn_backlog:建议设为 65535,应对突发 SYN 流量;
- net.core.somaxconn:必须 ≥ 应用设置的 SO_BACKLOG 值;
- net.ipv4.tcp_abort_on_overflow:设为 0(默认)表示静默丢包,设为 1 则发 RST,客户端报 connection reset;一般保持 0 即可;
- 如果网卡收包压力大,顺带检查 net.core.netdev_max_backlog 和 net.core.optmem_max 是否足够。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











