TCP 性能调优与内核参数
高并发服务器的 TCP 挑战
Web 服务器处理 10 万并发连接时,TCP 层面临:
- TIME_WAIT 堆积:主动关闭大量连接,端口耗尽
- 半连接队列溢出:SYN Flood 或突发流量导致 SYN Queue 满
- 全连接队列溢出:应用层处理慢,Accept Queue 满
- 内存压力:每个连接占用内核 TCB、发送/接收缓冲区
TIME_WAIT 调优
问题
高并发短连接场景(如 REST API),服务端主动关闭后产生大量 TIME_WAIT:
ss -tan | grep TIME-WAIT | wc -l
# 可能达到数万甚至数十万
TIME_WAIT 持续 2MSL(Linux 默认 60 秒),同一 {目的IP:目的端口} 组合下会占用本地端口。
调优参数
# 允许复用 TIME_WAIT 连接(仅出站连接,安全)
sysctl net.ipv4.tcp_tw_reuse=1
# 缩短 TIME_WAIT 持续时间(秒)
sysctl net.ipv4.tcp_fin_timeout=30
# 注意:tcp_tw_recycle 在 NAT 环境有风险,Linux 4.12+ 已移除
根本解决方案:
- HTTP Keep-Alive:复用 TCP 连接
- 连接池:数据库/缓存客户端维护长连接
- 让客户端主动关闭:服务端不进入 TIME_WAIT
SYN Flood 防护
半连接队列(SYN Queue)
服务端收到 SYN 后,连接进入 SYN_RCVD 状态,占用半连接队列。队列大小由 tcp_max_syn_backlog 控制。
全连接队列(Accept Queue)
三次握手完成后,连接进入 ESTABLISHED,等待应用层 accept()。队列大小由 min(somaxconn, backlog) 决定。
关键参数
# 半连接队列长度
sysctl net.ipv4.tcp_max_syn_backlog=65536
# 全连接队列长度(配合 listen() 的 backlog)
sysctl net.core.somaxconn=65535
# SYN Cookie 开启(SYN Flood 防护)
sysctl net.ipv4.tcp_syncookies=1
注意:listen(fd, backlog) 中的 backlog 是应用层传入的,实际队列大小 = min(somaxconn, backlog)。很多应用(如 Redis、Nginx)默认 backlog 较小,需同时调整应用配置。
缓冲区与内存调优
发送/接收缓冲区
# 全局缓冲区范围(字节)
sysctl net.ipv4.tcp_rmem="4096 87380 6291456" # 读缓冲区: min default max
sysctl net.ipv4.tcp_wmem="4096 65536 6291456" # 写缓冲区: min default max
# 自动调优(根据 RTT 和 BDP 动态调整)
sysctl net.ipv4.tcp_moderate_rcvbuf=1
缓冲区与 BDP 的关系:
最优缓冲区 = BDP = 带宽 × RTT
例如:带宽 1Gbps = 125MB/s, RTT = 100ms
BDP = 125MB/s × 0.1s = 12.5MB
缓冲区应 ≥ 12.5MB 才能充分利用带宽
其他关键内核参数
| 参数 | 默认值 | 建议值 | 作用 |
|---|---|---|---|
| tcp_timestamps | 1 | 1 | 启用时间戳选项(RTT测量+PAWS) |
| tcp_window_scaling | 1 | 1 | 启用窗口缩放 |
| tcp_sack | 1 | 1 | 启用选择性确认 |
| tcp_ecn | 0 | 2 | 启用 ECN(0=关闭, 1=开启, 2=被动) |
| tcp_fastopen | 1 | 3 | TFO 模式(1=客户端, 2=服务端, 3=双向) |
| tcp_retries2 | 15 | 8 | 放弃连接前的重传次数 |
| tcp_slow_start_after_idle | 1 | 0 | 空闲后是否重置 cwnd(建议 0) |
诊断工具
# 查看连接统计
ss -s
# 查看详细连接信息(含拥塞窗口)
ss -ti
# 查看 TCP 内存使用
cat /proc/net/sockstat
# 查看内核参数
cat /proc/sys/net/ipv4/tcp_*
# 实时观察 cwnd 变化(需 root)
tcpdump -i eth0 -w capture.pcap
# 用 Wireshark 分析: Statistics → TCP Stream Graphs → Window Scaling
本篇小结
- TIME_WAIT:用 Keep-Alive/连接池/客户端主动关闭解决,参数调优仅临时缓解
- SYN Queue + Accept Queue:分别控制半连接和全连接,需同时调大
- SYN Cookie:防止 SYN Flood,开启后无队列限制
- 缓冲区:应 ≥ BDP(带宽×RTT),自动调优通常足够
- 关键参数:timestamps、window_scaling、sack、ecn、fastopen
- 诊断:ss -s / ss -ti / /proc/net/sockstat
动手实践
查看当前 TCP 连接统计:
ss -s查看某条连接的详细参数(含 cwnd、rtt、ssthresh):
ss -ti | head -20批量调整内核参数(测试环境):
sudo sysctl -w net.ipv4.tcp_tw_reuse=1 sudo sysctl -w net.ipv4.tcp_fin_timeout=30 sudo sysctl -w net.core.somaxconn=65535 sudo sysctl -w net.ipv4.tcp_max_syn_backlog=65536思考:为什么
tcp_slow_start_after_idle建议设为 0?如果空闲 10 秒后 cwnd 重置为 1,对长连接应用有什么影响?