很多站长优化网站性能时,习惯把注意力放在 Nginx、PHP-FPM、数据库这些应用层组件上,却常常忽略了一个同样关键的因素——操作系统内核的网络参数。内核的网络栈承担着所有进出流量的转发与调度,默认参数是为通用场景设计的,对高并发、跨国访问、大量短连接的网站来说往往偏保守。这篇文章就从 TCP BBR 拥塞控制、文件句柄、连接队列、TIME_WAIT 这几个维度,讲讲个人站长如何安全、有效地调优内核网络参数。
一、为什么要调优内核网络参数
默认情况下,Linux 内核的一些限制对个人网站来说是够用的,但访问量一旦上来,几个典型的报警信号就会出现:网站间歇性出现 Connection refused、日志里报 Too many open files、并发稍高就 502、跨国访问吞吐上不去。这些现象背后,往往就是文件句柄上限太低、listen 队列太短、拥塞控制算法不适合高延迟链路等原因。
内核参数通过 sysctl 接口暴露,既可以临时修改(立即生效,重启丢失),也可以写入配置文件持久化(重启后依然生效)。在动手之前,请务必记住一条原则:每次只改一个或一组相关参数,改完立刻验证,出了问题能快速回滚。下面按主题逐个讲解。
二、开启 TCP BBR 拥塞控制算法
BBR 是 Google 在 2016 年开源的 TCP 拥塞控制算法,与传统的 CUBIC 不同,它不再以丢包作为判断网络拥塞的唯一信号,而是通过建模估算链路带宽与往返时延,从而更充分地利用带宽。对于带宽较高、延迟较大(例如跨国线路、家庭宽带上行)的场景,BBR 的提速效果非常明显,很多站长反馈开启后跨国下载和网页打开速度有明显改善。
开启 BBR 的前提是内核版本不低于 4.9,可以用 uname -r 查看。绝大多数云服务器(KVM、Xen 架构)都没问题,但 OpenVZ 架构的廉价 VPS 无法使用,因为它的内核由宿主机统一提供,这一点选购机器时要注意。
开启步骤如下:
modprobe tcp_bbr echo "net.core.default_qdisc=fq" >> /etc/sysctl.conf echo "net.ipv4.tcp_congestion_control=bbr" >> /etc/sysctl.conf sysctl -p
验证是否生效:
sysctl net.ipv4.tcp_congestion_control lsmod | grep bbr
如果第一行输出 bbr,说明系统默认拥塞算法已经是 BBR;第二行能看到 tcp_bbr 模块被加载。更精细的验证可以看单个连接的实际情况:ss -tin 会显示每个 TCP 连接的拥塞算法,确认新建连接都使用了 bbr。
需要说明的是,BBR 不是万能的。如果服务器带宽本身就很小(比如 1Mbps 的小水管),或者访问双方都在同一运营商的内网,BBR 的提升有限。另外,个别云厂商的网络架构下 BBR 效果不明显,这属于正常现象,可以对比开启前后的实测数据再决定是否保留。
顺带一提,早些年站长圈流行使用锐速(ServerSpeeder)这类商业加速模块,它是闭源软件,内核升级后经常失效,与云服务器虚拟化环境的兼容性也差。BBR 作为内核原生支持的算法,开源免费、无需额外安装、随内核升级自动兼容,如今已经成为事实上的标准。如果你的内核版本太老(低于 4.9)无法开启 BBR,与其折腾魔改版,不如直接把系统升级到新版发行版,一劳永逸。
三、文件句柄与进程连接数限制
高并发场景下最常见的报错之一是 Too many open files。每个 TCP 连接在服务端都会占用一个文件句柄,而 Linux 默认对单个进程的句柄数限制只有 1024(soft limit),对个人网站来说,这个数字随便一点流量就能打满。
系统级的总句柄上限由 fs.file-max 控制,一般不需要动,需要调整的是用户级限制。修改 /etc/security/limits.conf,在文件末尾加上:
* soft nofile 65535 * hard nofile 65535
修改后重新登录或重启生效,用 ulimit -n 验证。这里有一个容易踩的坑:如果网站是通过 systemd 管理的(Nginx、PHP-FPM 基本都是),limits.conf 对 systemd 服务不生效,必须在 service 文件里单独声明,例如在 /etc/systemd/system/nginx.service.d/limits.conf 中写入:
[Service] LimitNOFILE=65535
然后 systemctl daemon-reload 并重启 Nginx。验证进程实际限制可以用 cat /proc/进程PID/limits,看到 Max open files 变成 65535 才算真正生效。很多站长改了 limits.conf 发现没用,就是因为忘了 systemd 这一层。
四、TCP 连接队列与 SYN 洪泛防护
当连接请求来得太快,内核的 accept 队列(backlog)会被塞满,多余的新连接直接被丢弃,表现为客户端连接超时或立即被拒绝。两个相关参数值得调大:
net.core.somaxconn = 4096 net.ipv4.tcp_max_syn_backlog = 4096
somaxconn 是 listen 队列上限,Nginx 的 listen 指令默认也带一个 backlog 参数,两者要配合起来。如果 Nginx 配置里写了 listen 80 backlog=2048,那么内核 somaxconn 至少不能小于 2048,否则以小的为准。另外,确认 net.ipv4.tcp_syncookies 为 1(大多数发行版默认开启),它能在 SYN 洪水攻击时保护服务器不因半连接耗尽内存而崩溃。
队列溢出时,dmesg 里通常能看到 listen queue of a socket overflowed 之类的提示,ss -lnt 也可以看到 Send-Q 列数值偏大,这些都可以作为判断依据。
五、TIME_WAIT 与本地端口范围
很多站长一看到 ss 输出里大量 TIME_WAIT 状态连接就紧张,其实 TIME_WAIT 是 TCP 协议正常的状态,用于保证旧连接的报文不会串扰到新连接,是设计使然,不必大惊小怪。真正需要关注的是两种场景:一是 Nginx 作为反向代理时,主动向上游发起大量短连接,导致本地端口被 TIME_WAIT 占满,出现 Cannot assign requested address;二是高并发下连接数逼近上限。
针对端口耗尽,可以扩大本地端口范围:
net.ipv4.ip_local_port_range = 1024 65535
同时可以开启 tcp_tw_reuse,让内核复用处于 TIME_WAIT 状态的连接(仅对主动发起连接的一方有效,正好契合 Nginx 反代场景):
net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_fin_timeout = 30
特别提醒:千万不要开启 net.ipv4.tcp_tw_recycle,这个参数在 NAT 环境下会导致随机丢包,而且从内核 4.12 开始已经被移除,网上很多老教程还在推荐它,属于典型的过时误导。
六、TCP keepalive 与读写缓冲区
TCP keepalive 用于检测死连接,默认 7200 秒才探测一次,对服务器来说太长了,积累大量半开连接会白白占用资源。可以缩短:
net.ipv4.tcp_keepalive_time = 600 net.ipv4.tcp_keepalive_intvl = 30 net.ipv4.tcp_keepalive_probes = 3
读写缓冲区方面,可以适当提高内核 socket 缓冲上限,让大流量传输更顺畅:
net.core.rmem_max = 16777216 net.core.wmem_max = 16777216 net.ipv4.tcp_rmem = 4096 87380 16777216 net.ipv4.tcp_wmem = 4096 65536 16777216
注意,调大缓冲区会占用内存,低配服务器要量力而行,不要无脑把每个参数都调到最大。
七、完整配置示例与持久化
推荐把自定义参数集中放在 /etc/sysctl.d/ 目录下,而不是直接改 /etc/sysctl.conf,这样便于管理和排查。新建 /etc/sysctl.d/99-network.conf,内容如下:
net.core.default_qdisc = fq net.ipv4.tcp_congestion_control = bbr net.core.somaxconn = 4096 net.ipv4.tcp_max_syn_backlog = 4096 net.ipv4.ip_local_port_range = 1024 65535 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.core.rmem_max = 16777216 net.core.wmem_max = 16777216 net.ipv4.tcp_rmem = 4096 87380 16777216 net.ipv4.tcp_wmem = 4096 65536 16777216
执行 sysctl --system 让所有配置文件生效(等价于对每个文件执行 sysctl -p)。临时修改的参数如果不在配置文件里,重启后会丢失,所以持久化这一步一定要做。
八、调优前先测量,调优后再对比
调优不是玄学,一切以数据说话。动手之前,建议先记录基线:用 iperf3 测服务器与客户端之间的带宽,用 ping 测往返延迟,用 curl -w 测网站 TTFB。调优之后再做同样测试,对比数据判断每一项改动是否真的有收益。例如 BBR 开没开,iperf3 的吞吐数字会告诉你答案。
另外要提醒的是:网上的"一键优化脚本"和所谓大厂配置不要盲目照抄。服务器硬件、网络环境、业务形态各不相同,别人合适的参数到你这里可能是毒药。每改一个参数之前,先搞清楚它到底是干什么的,再决定要不要改。
九、常见问题
修改这些参数需要重启服务器吗?不需要。执行 sysctl -p 或 sysctl --system 后立即生效,只有写入配置文件,重启后才会保留。像 limits.conf 这类用户态限制,则需要重新登录或重启对应进程才会重新读取。
调错了参数导致网站异常怎么办?用 sysctl -w 把参数改回原值即可临时恢复,同时从配置文件中删掉对应行。所以动手之前一定要先记录原始值,或者给 /etc/sysctl.conf 做个备份。
云服务器上有些参数改了却不生效?这很正常。部分参数受虚拟化层或云厂商定制内核的限制,比如 OpenVZ 架构下无法更换拥塞控制算法。改完用 sysctl 查询当前生效值确认即可,确认不了的就接受它,不必强求。
十、总结
内核网络调优是一项性价比很高的投入,尤其是 TCP BBR 和文件句柄这两项,改动小、收益直接。本文涉及的参数都是个人站长场景下最常用、最安全的组合,照着配置基本不会出问题。最后再强调一遍:改参数前先备份配置文件、记录基线数据,改完验证、出了问题及时回滚,这才是运维的正道。