Linux 高并发服务器参数调优

Table of Contents

1. 文件描述符数量限制

文件描述符(FD)数量是限制并发连接数的关键因素之一。使用 ulimit -n 可以查看当前用户可以打开的最大 FD 数量,默认通常是 1024——这意味着单个进程最多同时处理 1024 个连接。对外开放的服务器应当扩大这个限制。

FD 限制分三层:内核全局上限、内核单进程上限、用户级限制。

1.1. 内核参数

# /etc/sysctl.d/90-fd.conf
# 仅在默认值不够时启用,以下数值为示例
# fs.file-max = 2000000
# fs.nr_open = 2000000
  • fs.file-max :内核全局最大文件描述符数量。现代发行版通常按内存自动计算默认值,一般已经很大。可以用 cat /proc/sys/fs/file-max 查看当前值,只有当默认值不满足需求时才需要手动设置。
  • fs.nr_open :单个进程可以打开的最大文件数量,默认值是 \(2^{20}=1048576\) 。它是 ulimit -n 的天花板——用户级 hard limit 不能超过这个值。如果你需要把 nofile 设置到超过 1048576,才需要先提高 fs.nr_open

验证:

sysctl fs.file-max fs.nr_open
cat /proc/sys/fs/file-nr  # 已分配 / 未使用 / 上限

1.2. 用户级限制(PAM)

# /etc/security/limits.d/90-nofile.conf
* soft nofile 600000
* hard nofile 600000
root soft nofile 600000
root hard nofile 600000

soft 是实际生效的限制——进程打开的 FD 数量达到 soft 后,再打开会直接失败(返回 EMFILE ),而不是仅仅发出警告。 hard 是 soft 的上限,普通用户只能降低 hard 而不能提高。将两者设为相同值意味着进程启动后就以最大额度运行。

注意:

  • /etc/security/limits.conf 可能被系统更新覆盖,建议写到 /etc/security/limits.d/90-nofile.conf
  • 通配符 * 不匹配 root 用户,所以上例额外给出了 root 的配置。
  • 有时还需要增加 nproc (最大进程数),但现代程序通常使用线程或异步 I/O,不太会触及这个限制。

1.3. systemd 服务限制

PAM limits 对 systemd 管理的服务 不生效 。systemd 启动服务时不走 PAM 登录流程,因此 /etc/security/limits.conf 里的配置对 Nginx、MySQL 等 daemon 无效。

对单个服务设置:

systemctl edit nginx
# 在打开的编辑器中添加:
# [Service]
# LimitNOFILE=600000
systemctl daemon-reload
systemctl restart nginx

对所有 systemd 服务设置全局默认:

# /etc/systemd/system.conf
DefaultLimitNOFILE=600000
  • 如果修改的是某个 unit 或 drop-in,执行 systemctl daemon-reload 后重启对应服务。
  • 如果修改的是 /etc/systemd/system.conf ,执行 systemctl daemon-reexec ,然后重启受影响的服务,或直接重启系统。
  • 最后用 cat /proc/<pid>/limits 验证实际生效值。

1.4. 如何估算 nofile

nofile 不该拍脑袋设一个大数,最好按业务形态粗略估算:

nofile >= 监听 socket + 峰值客户端连接数 + 峰值上游连接数 + 日志/临时文件 + 安全余量

对于单进程 event-loop 程序,最关键的是“客户端连接 + 上游连接”。反向代理会同时占用客户端和后端连接两侧的 FD,因此比纯 Web 服务更容易打满。

举个例子:如果 Nginx 峰值保持 100000 个客户端 keepalive 连接,同时与上游维持 20000 个 keepalive 连接,再加上监听 socket、日志文件和少量临时文件,那么 nofile 至少应在 120000 以上。通常再预留 20% 到 50% 的安全余量,设置到 150000 到 200000 会更稳妥。

2. 网络优化

以下参数主要面向高并发、大量短连接的场景(反向代理、API 网关、压测客户端等)。普通服务器不一定都需要调整——修改前请确认你的瓶颈确实在这里。

2.1. TIME_WAIT 相关

net.ipv4.tcp_tw_reuse = 1
  • tcp_tw_reuse :允许将 TIME_WAIT 状态的连接复用于新的 出站 连接。现代内核(约 2020 年后)默认值是 2 (仅对 loopback 生效)。设为 1 对所有出站连接启用,适合需要大量主动外连的场景(如反向代理连后端)。对于纯粹接受入站连接的服务(如 Web 服务器直接面向客户端),该参数帮助不大。
  • 关于 tcp_tw_recycle :该参数在 Linux 4.12 后 已被移除 。在 NAT 环境下会导致连接失败,不应再出现在配置中。

关于 TIME_WAIT 本身:TIME_WAIT 状态是 TCP 协议的正常机制——它防止旧连接的延迟报文被新连接误收。不要试图"消灭"所有 TIME_WAIT,而是通过 tcp_tw_reuse 让内核在安全的前提下复用它们。

2.2. 连接保活与超时

net.ipv4.tcp_keepalive_time = 600
net.ipv4.tcp_fin_timeout = 30
  • tcp_keepalive_time :TCP 发送 keepalive 探测的间隔,默认 7200 秒(2 小时)。改为 600 秒(10 分钟)可以更快发现死连接并回收资源。仅对启用了 SO_KEEPALIVE 的连接生效。副作用是增加少量网络流量。
  • tcp_fin_timeout :orphaned 连接(已被应用关闭引用的连接)在 FIN_WAIT_2 状态的超时时间,默认 60 秒。高并发短连接场景下可以缩短到 30 秒,加速连接回收。

2.3. 端口范围

net.ipv4.ip_local_port_range = 10000 65000

本机可用的临时端口号范围,默认通常是 32768 60999 。对于反向代理(如 Nginx 连后端)或压测客户端这类需要大量主动外连的角色,扩大范围可以支撑更多并发连接。纯粹接受入站连接的服务一般不需要调整。

2.4. 缓冲区

net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
net.core.rmem_default = 262144
net.core.wmem_default = 262144
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
  • tcp_rmem / tcp_wmem :TCP 接收/发送缓冲区的「最小值 默认值 最大值」(字节)。内核会在这个范围内自动调整(autotuning)。最大值设得太小会限制高带宽、长距离传输的吞吐量(BDP 受限)。上述值适合万兆网络环境;普通千兆服务器最大值设到 4194304 (4 MB)通常也够用。
  • rmem_max / wmem_max :内核允许的套接字缓冲区上限。需要不低于 tcp_rmem / tcp_wmem 的最大值,否则 TCP autotuning 会被截断。
  • rmem_default / wmem_default :非 TCP 套接字(如 UDP)的默认缓冲区大小。

2.5. 连接队列

net.core.somaxconn = 65535
net.core.netdev_max_backlog = 65535
  • somaxconn :=listen()= 的 backlog 上限。Linux 5.4 起默认值已从 128 提升为 4096。如果你的内核 ≥ 5.4 且并发不超过数千,可以不改。高并发场景建议设为 65535。注意应用层也要配合设置(如 Nginx 的 listen ... backlog=65535; )。
  • netdev_max_backlog :网卡收包后、内核协议栈处理前的缓冲队列长度,默认 1000。高流量网卡可以适当加大以减少丢包。

2.6. 默认已开启,无需手动设置

  • net.ipv4.tcp_window_scaling :支持超过 64KB 的 TCP 窗口(RFC 1323),自 Linux 2.6.8 起默认开启。除非有特殊兼容性需求,不需要写进配置。

3. 方便调试

# /etc/sysctl.d/90-debug.conf
kernel.core_uses_pid = 1
  • kernel.core_uses_pid :core dump 文件名添加 PID 后缀,防止多个进程的 core 文件互相覆盖。

如果需要完整的 core dump 支持,还需要配合:

# 允许生成 core 文件(默认可能为 0,即禁止)
ulimit -c unlimited

# 自定义 core 文件路径和命名格式
echo '/var/coredumps/core.%e.%p.%t' | sudo tee /proc/sys/kernel/core_pattern
sudo mkdir -p /var/coredumps

# 如果使用 systemd-coredump(多数现代发行版的默认方式)
# core dump 会被 journald 管理,用 coredumpctl 查看
coredumpctl list

4. 汇总

4.1. sysctl 配置

# /etc/sysctl.d/90-tuning.conf

# --- 文件描述符 ---
# 仅在默认值不够时取消注释
# fs.file-max = 2000000
# fs.nr_open = 2000000

# --- TCP TIME_WAIT ---
net.ipv4.tcp_tw_reuse = 1

# --- 连接保活与超时 ---
net.ipv4.tcp_keepalive_time = 600
net.ipv4.tcp_fin_timeout = 30

# --- 端口范围(主动外连场景) ---
net.ipv4.ip_local_port_range = 10000 65000

# --- 缓冲区 ---
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
net.core.rmem_default = 262144
net.core.wmem_default = 262144
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216

# --- 连接队列 ---
net.core.somaxconn = 65535
net.core.netdev_max_backlog = 65535

# --- 调试 ---
kernel.core_uses_pid = 1

应用配置:

sudo sysctl --system
# 验证
sysctl -a | grep -E 'somaxconn|file-max|tw_reuse'

4.2. 用户级 FD 限制

# /etc/security/limits.d/90-nofile.conf
* soft nofile 600000
* hard nofile 600000
root soft nofile 600000
root hard nofile 600000

4.3. systemd 服务 FD 限制

# /etc/systemd/system.conf(全局默认)
DefaultLimitNOFILE=600000

或对单个服务:

systemctl edit nginx  # 添加 [Service] LimitNOFILE=600000
systemctl daemon-reload
systemctl restart nginx

修改单个服务后执行 systemctl daemon-reload 并重启对应服务;修改 /etc/systemd/system.conf 后执行 systemctl daemon-reexec ,再重启受影响的服务或直接重启系统。

5. 验证清单

# 文件描述符
ulimit -n                        # 当前用户 soft limit
ulimit -Hn                       # 当前用户 hard limit
cat /proc/sys/fs/file-nr         # 系统级 FD 使用情况(已分配 / 未使用 / 上限)
cat /proc/<pid>/limits           # 特定进程的实际限制

# 网络参数
sysctl net.ipv4.tcp_tw_reuse
sysctl net.core.somaxconn
ss -s                            # TCP 连接状态统计

# core dump
ulimit -c                        # 是否允许生成 core
cat /proc/sys/kernel/core_pattern

By .