前言

压测时出现 Too many open files、大量 TIME_WAIT、连接队列溢出——这些都不是应用 bug,是内核默认参数太保守。生产服务器上线清单里,sysctl 调优应有一席之地。

一、sysctl 机制

# 查看
sysctl net.ipv4.tcp_tw_reuse            # 单个参数
sysctl -a | grep somaxconn              # 搜索

# 临时生效(重启丢失, 调试用)
sysctl -w net.core.somaxconn=4096

# 永久生效: 写入 /etc/sysctl.d/*.conf
echo "net.core.somaxconn = 4096" > /etc/sysctl.d/99-tuning.conf
sysctl --system                          # 重载全部配置

调整前先看现状:

ulimit -n                                # 当前 shell 的 fd 上限
cat /proc/sys/fs/file-nr                 # 已分配/未用/最大 fd
ss -s                                    # 连接概览
netstat -s | grep -iE "overflow|drop"    # 队列溢出统计(调优依据!)

二、必调参数详解

1. 文件描述符(万物的前提)

# /etc/sysctl.d/99-tuning.conf
fs.file-max = 1048576                    # 全系统 fd 上限
# 还要改用户级限制(systemd 服务走另一条路!)
cat >> /etc/security/limits.conf <<'EOF'
* soft nofile 65535
* hard nofile 65535
EOF

# systemd 管理的服务: 在 unit 里加
[Service]
LimitNOFILE=65535

常见坑:改了 limits.conf,systemd 服务没生效——cat /proc/<pid>/limits 验证实际值。

2. TCP 连接复用与 TIME_WAIT

net.ipv4.tcp_tw_reuse = 1        # 允许复用 TIME_WAIT 端口用于出站连接(客户端侧)
net.ipv4.tcp_fin_timeout = 30    # FIN_WAIT2 超时(默认60s)
net.ipv4.ip_local_port_range = 10240 65000   # 出站端口范围(短连接多时扩大)

TIME_WAIT 认知纠偏:

  • TIME_WAIT 是主动关闭方的正常状态(2MSL,约60s),是 TCP 可靠性的保障
  • 几万个 TIME_WAIT 本身不是故障;先治连接方式再调参数:
    • 短连接风暴 → 改长连接/连接池(应用层根治)
    • 出站端口耗尽 → tcp_tw_reuse=1 + 扩 ip_local_port_range
    • 服务器被动TIME_WAIT堆积 → 检查是否应用频繁主动断连

3. 监听与 accept 队列

net.core.somaxconn = 4096        # 全局监听队列上限(nginx listen backlog 不能超过它)
net.ipv4.tcp_max_syn_backlog = 8192    # 半连接队列(SYN 待完成握手数)
net.core.netdev_max_backlog = 16384    # 网卡收包队列

队列溢出的证据:

netstat -s | grep -i "listen"
#    XXX times the listen queue of a socket overflowed   ← 就是它!

4. TCP 缓冲区(大流量传输)

net.core.rmem_max = 16777216     # 套接字读缓冲上限
net.core.wmem_max = 16777216
net.ipv4.tcp_rmem = 4096 87380 16777216    # 最小/默认/最大(自适应)
net.ipv4.tcp_wmem = 4096 65536 16777216
net.ipv4.tcp_mtu_probing = 1    # MTU 黑洞探测(云上跨网传输卡住时救急)

5. TCP 保活与快速失败

net.ipv4.tcp_keepalive_time = 600      # 空闲600s后开始探测(默认7200太久)
net.ipv4.tcp_keepalive_intvl = 30
net.ipv4.tcp_keepalive_probes = 3      # 3次失败即断(90s发现死连接)
net.ipv4.tcp_syn_retries = 3           # 出站SYN重试(默认6次≈2分钟才报错)

6. 内存与 SWAP

vm.swappiness = 10               # 尽量少用swap(数据库/延迟敏感服务设1-10)
vm.overcommit_memory = 1         # Redis等场景需要(按需)
vm.max_map_count = 262144        # ES等大量mmap的程序需要

7. 安全相关(顺手加固)

net.ipv4.conf.all.rp_filter = 1          # 反向路径校验(防源地址欺骗)
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv4.tcp_syncookies = 1              # SYN Flood 防护(一般默认开)

三、落地模板

/etc/sysctl.d/99-tuning.conf(通用 Web/中间件服务器):

# ---- fs ----
fs.file-max = 1048576

# ---- core ----
net.core.somaxconn = 4096
net.core.netdev_max_backlog = 16384
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216

# ---- ipv4 tcp ----
net.ipv4.tcp_max_syn_backlog = 8192
net.ipv4.ip_local_port_range = 10240 65000
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_keepalive_time = 600
net.ipv4.tcp_keepalive_intvl = 30
net.ipv4.tcp_keepalive_probes = 3
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_mtu_probing = 1

# ---- vm ----
vm.swappiness = 10
sysctl --system                       # 应用并检查有无报错
sysctl net.core.somaxconn             # 验证

四、验证调优效果

# 1. 压测前后对比队列溢出计数
netstat -s | grep -i overflow

# 2. fd 使用量趋势
watch -n1 cat /proc/sys/fs/file-nr

# 3. TIME_WAIT 变化
ss -tan state time-wait | wc -l

# 4. 实测: ab/wrk 压 QPS
wrk -t4 -c1000 -d30s http://target/

五、调优原则(比参数本身重要)

  1. 有依据再调:netstat -s 的溢出/丢弃计数、ss -s 的连接分布——先诊断后开药
  2. 一次改一组:改完记录基准,可对比回滚
  3. 应用层优先:连接池、长连接、异步化能解决的,不推给内核
  4. 别抄"万能优化脚本":tcp_tw_recycle(已在 4.12 内核移除,开了反而 NAT 环境丢连接)就是血案

小结

症状 药方
Too many open files fs.file-max + limits + systemd LimitNOFILE
listen queue overflow somaxconn + 应用 backlog
出站端口耗尽 tcp_tw_reuse + ip_local_port_range
大文件传输慢 tcp_rmem/wmem
死连接挂半小时 keepalive 三件套
内存吃紧狂 swap vm.swappiness

本文是「Linux 运维」系列第 11 篇。