本文目录导读:

- 核心内核参数调整(
/etc/sysctl.conf) - PHP 应用层优化(根本性减少 TIME_WAIT)
- 负载均衡 / 反向代理层优化
- 监控与诊断命令
- 特殊场景优化建议
- 注意事项(避免挖坑)
- 最终优化 checklist
PHP项目中出现大量 TIME_WAIT 状态连接,通常是因为短连接(如使用 file_get_contents、curl 等发起 HTTP 请求)频繁建立和关闭 TCP 连接。TIME_WAIT 是 TCP 主动关闭连接的一方会进入的状态,持续 2MSL(通常为 2 分钟),目的是防止延迟的重复报文干扰新连接。
以下是针对 Linux 系统参数的优化方案(适用于 PHP 服务端场景,Nginx + PHP-FPM 或作为客户端发起大量 HTTP 请求):
核心内核参数调整(/etc/sysctl.conf)
允许 TIME_WAIT 端口重用(核心优化)
# 允许将 TIME_WAIT 状态的 socket 用于新建连接 net.ipv4.tcp_tw_reuse = 1
原理:主动关闭的连接在 TIME_WAIT 等待 2MSL 期间,若内核确认新连接与旧连接的源 IP/端口不同,可复用该端口。
加快 TIME_WAIT 回收(已弃用,谨慎使用)
注意:
tcp_tw_recycle在 Linux 4.12+ 内核中已移除(因 NAT 环境存在严重问题),若使用旧内核(3.x),可临时启用:net.ipv4.tcp_tw_recycle = 1替代方案:直接使用
tcp_tw_reuse和tcp_timestamps配合。
调整端口范围(增加可用端口,避免端口耗尽)
# 扩大本地可用临时端口范围(默认 32768-60999) net.ipv4.ip_local_port_range = 1024 65535
适用场景:PHP 作为客户端大量发起出站连接(如调用外部 API),端口可能被 TIME_WAIT 耗尽。
降低 FIN_WAIT2 超时(减少残留连接)
# 减少 FIN_WAIT2 状态等待时间(默认 60 秒) net.ipv4.tcp_fin_timeout = 15
完整配置示例追加到 /etc/sysctl.conf:
# 允许 TIME_WAIT 端口重用 net.ipv4.tcp_tw_reuse = 1 # 扩大临时端口范围 net.ipv4.ip_local_port_range = 1024 65535 # 降低 FIN_WAIT2 超时 net.ipv4.tcp_fin_timeout = 15 # 开启 TCP 时间戳(tcp_tw_reuse 依赖此项,默认已开启) net.ipv4.tcp_timestamps = 1 # 若内核 ≤ 4.11 且非 NAT 环境,可添加(新版已移除): # net.ipv4.tcp_tw_recycle = 0
应用配置
sysctl -p /etc/sysctl.conf
PHP 应用层优化(根本性减少 TIME_WAIT)
1 使用持久连接(长连接)
对于数据库(如 MySQL、Redis)和内部服务,使用持久连接显著减少连接建立/关闭:
// MySQL 持久连接
$pdo = new PDO('mysql:host=...', 'user', 'pass', [
PDO::ATTR_PERSISTENT => true
]);
// Redis 持久连接(默认会复用连接池)
$redis = new Redis();
$redis->pconnect('127.0.0.1', 6379);
2 HTTP 请求使用连接池(cURL 复用)
// 复用同一个 curl 句柄发起多次请求
$ch = curl_init();
// 设置复用连接
curl_setopt($ch, CURLOPT_FORBID_REUSE, false);
// 循环处理请求
foreach ($urls as $url) {
curl_setopt($ch, CURLOPT_URL, $url);
curl_exec($ch);
}
curl_close($ch);
3 关闭 Nagle 算法(高并发短连接)
若 PHP 作为服务端(如 Socket 服务器):
// 在 accept 后设置 TCP_NODELAY socket_set_option($socket, SOL_TCP, TCP_NODELAY, 1);
负载均衡 / 反向代理层优化
Nginx 优化(PHP 运行在 Nginx 后)
# 开启长连接到 PHP-FPM(upstream 配置)
upstream php_backend {
server 127.0.0.1:9000;
keepalive 32; # 保持 32 个空闲长连接
}
# 在 location 中设置 HTTP 版本
location ~ \.php$ {
fastcgi_pass php_backend;
fastcgi_keep_conn on; # PHP-FPM 支持 keep-alive
}
监控与诊断命令
查看 TIME_WAIT 数量
# 统计各状态连接数
ss -tan | awk '{print $1}' | sort | uniq -c
# 或使用 netstat(可能较慢)
netstat -an | grep TIME_WAIT | wc -l
查看具体发起 TIME_WAIT 的进程
# 显示 TIME_WAIT 连接对应的进程(需 root) ss -tanp | grep TIME_WAIT
查看端口占用情况
# 查看本地端口范围是否耗尽 cat /proc/sys/net/ipv4/ip_local_port_range # 查看当前使用中的临时端口数 ss -tan | wc -l
特殊场景优化建议
场景 A:PHP 作为客户端频繁调用外部 API
- 优先采用:
tcp_tw_reuse = 1+ip_local_port_range = 1024 65535 - 应用优化:使用连接池(如 Guzzle HTTP Client 的
curl句柄复用)
场景 B:PHP-FPM 作为服务端,短连接客户端涌入
- 检查 PHP-FPM 配置:
pm = dynamic+pm.max_children是否过高 - 开启 PHP-FPM 的 keep-alive:在
php-fpm.conf中设置pm.process_idle_timeout = 10s
场景 C:容器化环境(Docker/K8s)
- 容器内修改内核参数需使用
privileged模式或挂载sysctls - 建议在宿主机层面统一调整参数(特别是
ip_local_port_range)
注意事项(避免挖坑)
tcp_tw_recycle禁用原因:在 NAT 环境会导致连接失败,因为同一 NAT 网关后的多台机器会共享源 IP,但时间戳可能不一致。tcp_tw_reuse的限制:仅适用于客户端(主动发起连接方),服务端被动关闭的连接不受影响。- PHP-FPM 与长连接:若 PHP-FPM 进程数较多,长连接可能占用大量连接,需平衡
pm.max_children和backlog。 - 滑动窗口影响:调整
tcp_fin_timeout可能影响大文件传输的完整性,建议小幅度调整(15-30 秒)。
最终优化 checklist
# 1. 调整内核参数 echo "net.ipv4.tcp_tw_reuse = 1" >> /etc/sysctl.conf echo "net.ipv4.ip_local_port_range = 1024 65535" >> /etc/sysctl.conf echo "net.ipv4.tcp_fin_timeout = 15" >> /etc/sysctl.conf sysctl -p # 2. PHP-FPM 配置优化(/etc/php-fpm.d/www.conf) pm = dynamic pm.max_children = 50 pm.process_idle_timeout = 10s pm.max_requests = 500 # 3. 应用代码优化(使用持久连接、cURL 复用) # 4. 重启 PHP-FPM 验证 systemctl restart php-fpm # 5. 观察 TIME_WAIT 变化 watch -n 5 'ss -tan | grep TIME_WAIT | wc -l'
通过以上组合优化,通常可以将 TIME_WAIT 数量降低 80%-90%,且不影响业务稳定性,若仍有大量 TIME_WAIT,需检查是否有连接泄漏(如未正确关闭 cURL 句柄、数据库连接等)。