很多服务端程序上线后表现很怪:压测时 QPS 上不去、长连接跑着跑着就大面积超时、新连接偶尔被悄悄丢掉。代码没改,机器 CPU 和内存也都没满,问题往往出在Linux 内核的默认网络参数太保守。
对外业务(尤其是高并发短连接 + 海量长连接的混合场景)下,内核的半连接队列、TIME_WAIT 回收、TCP 缓冲区、拥塞控制算法都是按"通用服务器"预设的,直接拿来跑真实流量常常不够。这篇文章把生产环境里最该调的几个 sysctl 参数一次讲透,并给出一段可直接落地的配置。
一、先看清默认参数为什么不够
Linux 的默认网络栈是为"能跑通"设计的,不是为"高并发跑满"设计的。几个典型瓶颈:
- 连接队列太小 :
net.core.somaxconn默认往往只有 128,高并发下新连接会在内核队列排队甚至被丢弃。
- TIME_WAIT 堆积:短连接密集型服务,端口很快被 TIME_WAIT 占满,新连接"卡住连不上"。
- 缓冲区偏小 :
tcp_rmem/tcp_wmem默认上限低,大带宽、高延迟链路上吞吐上不去。
- 拥塞控制保守 :默认
cubic在长肥管道(高带宽时延积)下恢复慢。
调参不是"越大越好",而是让内核参数匹配你的真实流量特征。
二、连接队列:别让新连接死在门口
# 查看当前值
sysctl net.core.somaxconn
sysctl net.core.netdev_max_backlog
sysctl net.ipv4.tcp_max_syn_backlog
# 临时生效
sudo sysctl -w net.core.somaxconn=65535
sudo sysctl -w net.core.netdev_max_backlog=65535
sudo sysctl -w net.ipv4.tcp_max_syn_backlog=65535
somaxconn 控制全连接队列(accept 队列)上限,注意应用层监听时的 backlog 参数也不能小于它,否则会被截断。tcp_max_syn_backlog 是半连接队列,防御 SYN 洪峰 + 高并发建连都靠它。netdev_max_backlog 是网卡收包软中断的处理队列,网卡扛量大的服务要调高。
同时别忘文件描述符上限(每个连接都要一个 fd):
# 查看系统级 fd 上限
cat /proc/sys/fs/file-max
# 临时调高
sudo sysctl -w fs.file-max=1000000
应用自身的 ulimit -n 也要同步放开,否则内核放开了、进程还是建不了连接。
三、TIME_WAIT 与端口复用:短连接服务的命门
短连接(每次请求新建连接、用完即关)会留下大量 TIME_WAIT。默认 60 秒才回收,高 QPS 下本地端口瞬间被占满,表现为"明明对端没挂,自己却连不出去"。
# 开启 TIME_WAIT 快速复用(仅对客户端出站连接有效)
sudo sysctl -w net.ipv4.tcp_tw_reuse=1
# 缩短 FIN 等待
sudo sysctl -w net.ipv4.tcp_fin_timeout=30
tcp_tw_reuse=1 依赖时间戳(net.ipv4.tcp_timestamps=1,默认开),它允许在 TIME_WAIT 状态下安全地复用 socket 给新出站连接,对"自己作为客户端去连对端服务"的场景最有效。
⚠️ 注意:tcp_tw_reuse 只作用于出站,不要和当年被废弃的 tcp_tw_recycle(早已从内核移除)混淆。服务端接受入站连接时,TIME_WAIT 回收主要靠调大 tcp_max_tw_buckets 容忍堆积,而不是强行快速回收。
容忍堆积,而不是强行快速回收。
|-----------------------------|-----|-------------|---------------------|
| 参数 | 默认值 | 建议值(高并发) | 作用 |
| net.ipv4.tcp_tw_reuse | 0 | 1 | 出站连接 TIME_WAIT 安全复用 |
| net.ipv4.tcp_fin_timeout | 60 | 30 | 缩短 FIN 后等待 |
| net.ipv4.tcp_max_tw_buckets | 波动 | 可放宽到 200000 | 容忍更多 TIME_WAIT 共存 |
四、长连接保活:空闲连接别被静默掐断
长连接(推送、WebSocket、消息队列)空闲一段可能被中间设备静默掐断。内核级保活间隔默认是 7200 秒(两小时),关键业务等不起。
sudo sysctl -w net.ipv4.tcp_keepalive_time=600
sudo sysctl -w net.ipv4.tcp_keepalive_intvl=30
sudo sysctl -w net.ipv4.tcp_keepalive_probes=5
这组值表示空闲 10 分钟开始探测、每 30 秒一次、连续 5 次无响应判定死亡。应用层最好再叠一层心跳(如每 15--30 秒 ping/pong),传输层保活兜底、应用层心跳快速感知,双保险。
另外 net.ipv4.tcp_retries2 控制丢包后重传次数,默认 15 次在弱网长连接下会导致一条死连接很久才被放弃,可适当下调到 8,让故障更快暴露:
sudo sysctl -w net.ipv4.tcp_retries2=8
五、缓冲区与吞吐:把带宽用满
高带宽、高延迟的对外链路(长肥管道)下,TCP 窗口太小会喂不饱带宽。调大收发缓冲区:
sudo sysctl -w net.ipv4.tcp_rmem="4096 87380 67108864"
sudo sysctl -w net.ipv4.tcp_wmem="4096 65536 67108864"
sudo sysctl -w net.ipv4.tcp_window_scaling=1
三个值分别是 min / default / max(字节)。max 调到 64MB 后配合窗口缩放,单连接在大管道上能跑出更高吞吐。内核还会根据内存压力自动在 min~max 间调节,所以给一个足够大的上限即可。
net.ipv4.tcp_mem 是系统级 TCP 内存页数上限(按页计),内存充裕的机器可放宽,避免大量连接时内核主动丢包:
# 单位:页(通常 4KB/页),示例为 4G/6G/8G 字节换算
sudo sysctl -w net.ipv4.tcp_mem="1048576 1572864 2097152"
六、拥塞控制:长肥管道换 BBR
默认 cubic 在丢包后窗口回退激进,高带宽时延积链路上恢复慢。对"对外链路延迟大、但带宽足"的场景,换成 BBR 收益明显:
# 确认内核已加载 bbr
ls /proc/sys/net/ipv4/tcp_congestion_control
# 查看可用算法
sysctl net.ipv4.tcp_available_congestion_control
# 临时切换
sudo sysctl -w net.ipv4.tcp_congestion_control=bbr
BBR 基于带宽和 RTT 建模而非单纯丢包,弱网和高延迟链路上吞吐与稳定性更好。切换前确认内核支持(4.9+ 默认编译进 bbr)。
此外关闭空闲后慢启动,避免长连接空闲重传后又从很小窗口起步:
sudo sysctl -w net.ipv4.tcp_slow_start_after_idle=0
七、写成永久配置
临时 sysctl -w 重启就丢。生产环境一律写配置文件:
sudo tee /etc/sysctl.d/90-net-tuning.conf > /dev/null <<'EOF'
# 连接队列
net.core.somaxconn=65535
net.core.netdev_max_backlog=65535
net.ipv4.tcp_max_syn_backlog=65535
# TIME_WAIT 复用
net.ipv4.tcp_tw_reuse=1
net.ipv4.tcp_fin_timeout=30
net.ipv4.tcp_max_tw_buckets=200000
# 长连接保活
net.ipv4.tcp_keepalive_time=600
net.ipv4.tcp_keepalive_intvl=30
net.ipv4.tcp_keepalive_probes=5
net.ipv4.tcp_retries2=8
# 缓冲区
net.ipv4.tcp_rmem=4096 87380 67108864
net.ipv4.tcp_wmem=4096 65536 67108864
net.ipv4.tcp_window_scaling=1
net.ipv4.tcp_mem=1048576 1572864 2097152
# 拥塞控制
net.ipv4.tcp_congestion_control=bbr
net.ipv4.tcp_slow_start_after_idle=0
fs.file-max=1000000
EOF
# 加载生效
sudo sysctl -p /etc/sysctl.d/90-net-tuning.conf
建议文件名用 90- 前缀,确保排在默认配置之后加载、不被覆盖。
八、改完怎么验证
队列是否生效:
ss -lnt | head # Send-Q 列应体现 somaxconn 上限
sysctl net.core.somaxconn
当前在用拥塞算法与连接状态抽样:
sysctl net.ipv4.tcp_congestion_control
ss -i | grep -o 'bbr\|cubic' | sort | uniq -c
TIME_WAIT 数量是否收敛:
ss -ant | awk '{print $1}' | sort | uniq -c
上线前务必在预发环境 按真实流量比例压一轮,观察 ss -ant 的状态分布、重传率(nstat -az TcpRetransSegs)和 P99 延迟,确认没有把队列调到溢出或内存吃满再推生产。
小结
sysctl 调优的本质是让内核默认假设匹配你的真实流量画像:高并发短连接重点放开队列 + 复用 TIME_WAIT;长连接重点压低保活间隔 + 快速失败;大管道重点放大缓冲区 + 切 BBR。改之前先量、改之后先压测再上线,别照抄网上"万能配置"------不同业务对连接模型和带宽时延积的敏感度差很远。把这几组参数调对,很多"莫名其妙的连接超时和吞吐上不去"会从根上消失。