Nginx加PHP高并发部署,连接数与缓存参数如何优化?
优化 Nginx 加 PHP 的高并发部署,目标不是把所有连接数和超时都调大,而是让连接接入、PHP 执行、页面缓存和静态资源分工明确:Nginx 承接空闲连接与静态请求,PHP-FPM 只处理必要的动态计算,缓存减少重复执行,超时及时释放失效请求。
下面以香港服务器上的 Ubuntu Server 24.04 LTS、Nginx、PHP 8.3-FPM 为操作环境,给出可验证、可回滚的配置步骤。香港机房的位置不会改变 Nginx 参数的含义,但访问线路、跨境网络时延和出口带宽会影响连接占用与页面加载,因此服务器侧调优必须与目标访问地区的网络验证分开进行。
针对 Nginx 与 PHP-FPM 组合下的网站、业务后台和数据库负载,A5数据提供香港、美国、日本、韩国、中国台湾、新加坡及马来西亚等地区的物理服务器资源,覆盖 Xeon Gold、AMD EPYC、SSD/NVMe 存储及不同内存配置,并配套多种带宽线路选择。多地区部署与从入门建站到高规格计算的产品组合,可为高并发连接、PHP 多进程运行、数据库缓存和多任务业务提供相应的硬件与网络基础。
一、准备环境、配置备份与资源预算
1. 确认系统与部署边界
新部署可优先选择 Ubuntu Server 24.04 LTS 或 Debian 12,前者的发行版软件包通常对应 PHP 8.3,后者通常对应 PHP 8.2。已有 Rocky Linux、AlmaLinux 环境不必为了调优更换系统,但其 PHP 软件源、服务名称、配置路径和安全策略需要单独核对,不能直接照搬本文命令。
本文命令适用于 Ubuntu 24.04,操作前应满足以下条件:
- 已安装 Nginx、PHP-FPM,应用能够正常访问。
- 已有测试环境或维护窗口,并保留 SSH 会话及控制台入口。
- 已确认域名、网站根目录、PHP-FPM 套接字路径。
- 数据库、会话存储和应用配置已有独立备份。
- 压测只针对自己有权限的服务,不直接向生产站点灌入大量流量。
检查实际版本和资源:
cat /etc/os-release
nginx -v
php-fpm8.3 -v
nproc
free -h
df -h / /var/cache/nginx
systemctl status nginx php8.3-fpm --no-pager
CLI 的 PHP 配置不一定与 FPM 相同,后续配置应修改 /etc/php/8.3/fpm/,不要只修改 CLI 的 php.ini。
下文使用 /etc/nginx/sites-available/web.conf 表示已有且已启用的站点配置,网站根目录为 /srv/www/web/public。操作时必须替换为真实路径;不要同时保留两个处理同一域名的 server。
2. 备份会修改的文件
进入 root shell,备份配置并记录内核参数:
sudo -i
BACKUP="/root/web-tuning-$(date +%Y%m%d-%H%M%S)"
mkdir -p "$BACKUP"
cp -a /etc/nginx "$BACKUP/nginx"
cp -a /etc/php/8.3/fpm "$BACKUP/php-fpm"
mkdir -p "$BACKUP/systemd"
if [ -d /etc/systemd/system/nginx.service.d ]; then
cp -a /etc/systemd/system/nginx.service.d "$BACKUP/systemd/"
fi
if [ -f /etc/sysctl.d/90-web-capacity.conf ]; then
cp -a /etc/sysctl.d/90-web-capacity.conf "$BACKUP/"
fi
sysctl -n net.core.somaxconn > "$BACKUP/somaxconn.before"
nginx -T > "$BACKUP/nginx-expanded.txt" 2>&1
printf '%s\n' "$BACKUP"
记下最后输出的目录。以上操作只复制配置,不改变服务状态;备份中可能包含敏感配置,应保留 root 访问权限,不放进网站目录。
3. 分开估算连接容量与 PHP 并发
两者不能使用同一个数字:
| 参数 | 约束对象 | 主要预算依据 | 验证方法 |
|---|---|---|---|
worker_connections | 单个 Nginx worker 的连接槽位 | 客户端连接、上游连接、文件描述符 | 错误日志、进程限制、连接状态 |
pm.max_children | 同时执行 PHP 的子进程 | 单进程内存、CPU、数据库承载 | FPM 状态、内存、请求时延 |
| 缓存空间 | 可重复利用的响应或文件信息 | 内容数量、体积、更新频率 | HIT 比例、磁盘空间、内容正确性 |
worker_processes × worker_connections 只能作为连接槽位的粗略上界,不能直接解释为 PHP 并发数。动态请求可能同时占用客户端连接与 FastCGI 上游连接,还受文件描述符、内存及 PHP 执行能力限制。

例如,一台 4 vCPU、8 GiB 内存的服务器,为 PHP 留出 4 GiB,预热后单个 PHP 进程的 RSS 约为 80 MiB,则按内存估算:
4096 MiB ÷ 80 MiB ≈ 51 个进程。
这是粗估的内存边界,不是推荐配置。考虑 CPU、数据库和请求波动,可以先从 pm.max_children = 32 验证。RSS 包含共享页,简单相加会有偏差;精细评估可使用 PSS,并检查整机实际可用内存。
二、优化 Nginx 连接接入,每次修改后验证
1. 调整文件描述符与监听队列
先查看当前状态:
systemctl show nginx -p LimitNOFILE
sysctl net.core.somaxconn
sysctl fs.nr_open
ss -lnt
对本文示例,将 Nginx 服务的文件描述符上限设置为 65535。在创建文件前确认同名文件不存在;若已存在,应合并内容,而不是覆盖其他设置。
mkdir -p /etc/systemd/system/nginx.service.d
cat > /etc/systemd/system/nginx.service.d/90-web-capacity.conf <<'EOF'
[Service]
LimitNOFILE=65535
EOF
若 net.core.somaxconn 已达到或高于 4096,无须降低它。仅在当前值偏小、确有突发连接需求时,创建以下持久化配置:
cat > /etc/sysctl.d/90-web-capacity.conf <<'EOF'
net.core.somaxconn = 4096
EOF
sysctl -p /etc/sysctl.d/90-web-capacity.conf
此操作改变本机监听队列上限,不会提升 PHP 执行能力。不要顺手套用大量 TCP 参数,也不要把端口耗尽与入站连接容量混为一谈。
systemd 服务限制需要重启 Nginx 才能完整生效,单纯 reload 不够。先完成下面的配置检查,再安排维护窗口执行重启。
2. 配置 worker、Keepalive 与基础传输参数
编辑 /etc/nginx/nginx.conf,在对应层级修改或合并以下参数,不要追加重复的 events 或 http 块:
user www-data;
worker_processes auto;
worker_rlimit_nofile 65535;
events {
worker_connections 8192;
multi_accept off;
}
http {
sendfile on;
tcp_nopush on;
keepalive_timeout 15s;
keepalive_requests 1000;
client_header_timeout 10s;
client_body_timeout 15s;
send_timeout 15s;
reset_timedout_connection on;
# 保留原有 mime.types、日志、站点 include 等配置
}
这是一段需要合并的配置片段,不是完整的 nginx.conf。各参数的适用边界如下:
worker_connections 8192:示例起点,文件描述符必须配套;不是越大越好。keepalive_timeout 15s:减少闲置连接停留时间。重复请求较密集的站点可以适当延长,但需要观察连接占用。keepalive_requests 1000:限制同一持久连接承载的请求次数,不代表同时允许 1000 个请求。client_body_timeout:两次请求体读取之间的超时,不是整个上传的总时限。send_timeout:两次写入之间的超时,不是整份响应必须在 15 秒内传完。
慢速上传、长轮询和流式响应应使用独立 location 与参数,不应直接套用普通页面的短超时。
验证并应用:
nginx -t
systemctl daemon-reload
systemctl restart nginx
systemctl is-active nginx
PID=$(systemctl show nginx -p MainPID --value)
grep 'Max open files' "/proc/$PID/limits"
预期语法测试成功、服务状态为 active,进程限制中出现相应的文件描述符上限。若启动失败,先恢复配置,不要反复重启掩盖错误。
三、配置 PHP-FPM,避免请求进入无效排队
1. 根据内存预算设置进程池
编辑 /etc/php/8.3/fpm/pool.d/www.conf,保留原有用户、套接字权限和环境配置,调整以下项目:
listen = /run/php/php8.3-fpm.sock
listen.backlog = 1024
pm = dynamic
pm.max_children = 32
pm.start_servers = 8
pm.min_spare_servers = 4
pm.max_spare_servers = 12
pm.max_requests = 500
request_terminate_timeout = 30s
request_slowlog_timeout = 3s
slowlog = /var/log/php8.3-fpm-www-slow.log
pm.status_path = /fpm-status
ping.path = /fpm-ping
dynamic 适合持续有访问且希望保留部分空闲进程的站点;ondemand 能减少低流量时的驻留内存,但突发流量可能承担进程创建开销。
pm.max_requests 用于周期性回收进程,可缓解部分扩展或应用的内存增长,但不能修复泄漏。listen.backlog 只是排队容量:队列加长后,用户可能等待更久,并不代表吞吐提高。
request_terminate_timeout = 30s 会终止超过阈值的请求。支付回调、批量导出等长任务应单独评估;更合适的做法通常是将长任务交给队列,而不是无限放宽网页超时。
2. 开启并核验 OPcache
先检查 FPM 是否加载 OPcache:
php-fpm8.3 -i | grep -E 'opcache.enable|opcache.memory_consumption'
在确认同名文件不存在或已备份后,创建 /etc/php/8.3/fpm/conf.d/99-web-tuning.ini:
opcache.enable=1
opcache.memory_consumption=128
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=20000
opcache.validate_timestamps=1
opcache.revalidate_freq=2
max_execution_time=25
OPcache 缓存 PHP 编译后的字节码,不缓存用户页面,也不替代数据库缓存。内存大小应依据应用脚本数量和 OPcache 状态调整。
保留时间戳检查,更适合常规发布流程;关闭检查后若没有可靠的缓存重置或 FPM 重载步骤,可能继续执行旧代码。另外,Linux 下 max_execution_time 不能作为所有外部等待的可靠总时限,仍需应用层数据库、HTTP 客户端超时配合。
检查并重载:
php-fpm8.3 -t
systemctl reload php8.3-fpm
systemctl is-active php8.3-fpm
若重载后新旧进程短时并存,应等待旧请求退出后再观察内存。不要在高负载时连续重载。
3. 为 FastCGI 设置合理超时与缓冲
在已有站点的 PHP 处理位置合并:
location ~ \.php$ {
try_files $uri =404;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
fastcgi_connect_timeout 2s;
fastcgi_send_timeout 10s;
fastcgi_read_timeout 35s;
fastcgi_buffering on;
fastcgi_buffer_size 16k;
fastcgi_buffers 16 16k;
fastcgi_busy_buffers_size 64k;
}
此示例适用于文件确实位于网站根目录下的常规 PHP 应用;使用发布目录软链接、特殊路由或框架专用配置时,应保留经过验证的脚本路径规则。
FastCGI 缓冲可减少慢客户端持续占用 PHP 输出过程的机会,但会消耗内存,较大响应还可能落入临时文件。流式接口应单独配置,不要全站关闭缓冲。
这里的 fastcgi_read_timeout 比 FPM 终止阈值稍长,便于控制异常请求;它同样是两次读取之间的超时,不是严格的响应总时限。
四、配置静态缓存、压缩与可控的页面缓存
1. 先优化不需要执行 PHP 的请求
在 http 层合并压缩与文件信息缓存:
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_comp_level 4;
gzip_types text/plain text/css application/javascript
application/json image/svg+xml application/xml;
open_file_cache max=10000 inactive=30s;
open_file_cache_valid 30s;
open_file_cache_min_uses 2;
open_file_cache_errors off;
压缩重点是文本资源。JPEG、WebP、压缩包等通常不值得再次压缩;提高压缩级别可能增加 CPU 开销。对于包含敏感数据、且同时反射可控输入的响应,还应评估压缩侧信道风险,不宜只看节省了多少流量。
open_file_cache 缓存文件描述符和相关元数据,不是页面内容缓存。快速发布、文件原地替换或负缓存配置不当时,可能影响新文件可见性,因此示例不缓存文件查找错误。
在站点中,为专用的版本化资源目录配置:
location /assets/ {
try_files $uri =404;
expires 30d;
}
该配置仅适合文件名带内容版本的资源,例如 app.a18f2c.js。若文件名固定且经常更新,应缩短缓存时间;不要让 HTML、登录页面和用户私有下载继承长期缓存。

2. 只缓存明确公开的动态页面
页面缓存是降低 PHP 压力的重要手段,也是容易造成数据泄露的环节。只有确认响应与登录身份、Cookie、IP、语言等请求属性无关时,才能启用共享缓存。
以下示例仅允许无查询参数的公开新闻详情页 /news/数字 进入缓存。先创建目录,权限变更仅作用于新建缓存目录:
install -d -o www-data -g www-data -m 0750 /var/cache/nginx/public
在 http 层添加:
fastcgi_cache_path /var/cache/nginx/public
levels=1:2
keys_zone=PUBLIC:32m
inactive=30m
max_size=1g
use_temp_path=off;
map $request_method $skip_method {
default 1;
GET 0;
HEAD 0;
}
map $http_cookie $skip_cookie {
default 1;
"" 0;
}
map $http_authorization $skip_auth {
default 1;
"" 0;
}
map $uri $skip_page {
default 1;
~^/news/[0-9]+/?$ 0;
}
map $args $skip_args {
default 1;
"" 0;
}
keys_zone=PUBLIC:32m 是缓存键元数据内存,max_size=1g 是磁盘响应缓存规模,两者不是同一个容量。磁盘清理并非瞬间完成,应保留额外空间。
在前面的 PHP location 内继续添加:
fastcgi_cache PUBLIC;
fastcgi_cache_key "$scheme|$host|$request_uri";
fastcgi_cache_methods GET HEAD;
fastcgi_cache_valid 200 30s;
fastcgi_cache_lock on;
fastcgi_cache_lock_timeout 3s;
fastcgi_cache_bypass
$skip_method $skip_cookie $skip_auth $skip_page $skip_args;
fastcgi_no_cache
$skip_method $skip_cookie $skip_auth $skip_page $skip_args
$upstream_http_set_cookie;
add_header X-FastCGI-Cache $upstream_cache_status always;
这里同时控制“是否读取缓存”和“是否写入缓存”。只设置其中一项,无法完整隔离登录请求。
不要添加忽略 Cache-Control 或 Set-Cookie 的规则。后台、购物车、账户、支付接口以及依赖用户身份的 API 均不应进入此共享缓存。即使没有 Cookie,如果内容按地区或请求头变化,也必须调整缓存设计,不能直接使用本例。
验证配置后重载:
nginx -t && systemctl reload nginx
对于确实满足缓存条件的页面,首次访问通常为 MISS,再次访问可能为 HIT;携带 Cookie 的请求应为 BYPASS。如果应用返回 private、no-store 或设置 Cookie,页面不进入缓存是合理结果,不应强行绕过。

五、逐层验证并处理常见异常
1. 建立仅本机可访问的状态入口
在站点文件中增加独立的回环监听服务器。先确认 8081 端口未被其他服务占用:
ss -lnt | grep ':8081'
无占用后添加:
server {
listen 127.0.0.1:8081;
server_name localhost;
location = /nginx-status {
stub_status;
}
location = /fpm-status {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}
}
此入口不要绑定公网地址。FPM 状态页包含进程与请求信息,不能作为公开调试页面。
nginx -t && systemctl reload nginx
curl -sS http://127.0.0.1:8081/nginx-status
curl -sS http://127.0.0.1:8081/fpm-status
重点观察 FPM 的 listen queue、active processes、max children reached。后者是累计计数,应比较观察窗口前后的增量,而不是看到非零值就立即扩容。普通状态请求也可能因进程池饱和而等待。
2. 验证功能、缓存与压缩
以下命令使用本机 HTTP 测试入口;生产 HTTPS 还需单独验证证书、协议和真实域名访问。不要为了复制示例而移除已有 TLS 配置。
curl -sS -D - -o /dev/null \
-H 'Host: web.example.com' \
http://127.0.0.1/news/123
curl -sS -D - -o /dev/null \
-H 'Host: web.example.com' \
-H 'Cookie: test=1' \
http://127.0.0.1/news/123
curl --compressed -sS -D - -o /dev/null \
-H 'Host: web.example.com' \
http://127.0.0.1/assets/app.a18f2c.js
替换为真实域名和已存在的文件。预期分别检查缓存状态、Cookie 绕过状态,以及 Content-Encoding: gzip、静态缓存头。小于压缩阈值的文件不出现 gzip 并不代表配置失败。
3. 按低风险到高风险排查故障
| 现象 | 先检查什么 | 处理方向 |
|---|---|---|
| 连接超时 | DNS、线路、监听端口、既有访问控制 | 先确认请求能到服务器,不急于修改 PHP |
| 502 | FPM 状态、套接字路径和权限 | 修复服务或路径;Permission denied 不靠加大超时解决 |
| 504 | 慢日志、数据库耗时、外部调用超时 | 先减少上游等待,避免只放宽 Nginx 超时 |
worker_connections are not enough | 当前连接数、worker 配置、文件限制 | 排除异常连接后,再增加容量 |
max children reached 持续增长 | CPU、内存、FPM 队列、数据库连接 | 有余量才增进程,否则优化或限流 |
| 缓存一直 MISS | Cookie、响应头、缓存目录权限 | 核实是否符合缓存条件,不强制缓存私有内容 |
| 发布后内容滞后 | 浏览器缓存、页面缓存、OPcache | 分层确认,不直接清空所有缓存 |
常用检查命令:
journalctl -u nginx -u php8.3-fpm --since "-10 min" --no-pager
tail -n 100 /var/log/nginx/error.log
tail -n 100 /var/log/php8.3-fpm-www-slow.log
vmstat 1 10
ss -s
如果提高 PHP 并发后数据库连接、CPU 和时延一起上升,应退回原值。此时更多进程只是把等待压力转移到了数据库。
六、结果检查与回滚清单
1. 分级检查优化结果
在预发布环境按逐级并发测试,每档持续数分钟,分别测试静态资源、缓存命中的公开页面和不可缓存的动态接口。压测机应独立于被测服务器,并为错误率、时延和资源占用设置停止条件。
验收不能只看平均响应时间,应至少检查:
- 配置测试通过,Nginx、PHP-FPM 状态正常,无新增 502、504。
- P95、P99 时延未因排队明显恶化。
- FPM 队列不会持续增长,
max children reached没有持续新增。 - 内存保留余量,没有 OOM 或持续大量 swap 换入换出。
- 登录与未登录响应隔离,公开缓存不会返回其他用户的数据。
- 新版本静态资源和 PHP 代码可以按发布流程及时生效。
- 缓存目录、临时文件和日志所在磁盘空间充足。
香港服务器还应从目标访问地区验证 DNS、TLS 握手和下载表现。出口带宽、线路时延与后端执行时间要分别观察,不能用本机压测替代真实网络测试。
例如,按十进制计算,100 Mbps 链路传输约 200 KB 的响应,忽略协议开销时,带宽上限约为:
100,000,000 bit/s ÷(200,000 byte × 8 bit/byte)≈ 62.5 次/秒。
这只是带宽估算,不是服务器实测容量。压缩、浏览器缓存和 CDN 可以减少源站传输量,但不能修复慢 SQL。
2. 执行配置回滚
以下操作适用于需要恢复本次配置的维护窗口。先将 BACKUP 设置为实际备份目录;目录移动会短暂改变配置路径,不应与其他发布任务同时进行。
BACKUP="/root/web-tuning-实际时间"
FAILED="/root/web-tuning-failed-$(date +%Y%m%d-%H%M%S)"
mkdir -p "$FAILED"
mv /etc/nginx "$FAILED/nginx"
cp -a "$BACKUP/nginx" /etc/nginx
mv /etc/php/8.3/fpm "$FAILED/php-fpm"
cp -a "$BACKUP/php-fpm" /etc/php/8.3/fpm
保留失败配置,便于后续分析。然后恢复本次涉及的 systemd 与 sysctl 文件;若备份前不存在该文件,则将新增文件移出生效目录:
if [ -d /etc/systemd/system/nginx.service.d ]; then
mv /etc/systemd/system/nginx.service.d "$FAILED/nginx.service.d"
fi
if [ -d "$BACKUP/systemd/nginx.service.d" ]; then
cp -a "$BACKUP/systemd/nginx.service.d" \
/etc/systemd/system/nginx.service.d
fi
if [ -f /etc/sysctl.d/90-web-capacity.conf ]; then
mv /etc/sysctl.d/90-web-capacity.conf "$FAILED/"
fi
if [ -f "$BACKUP/90-web-capacity.conf" ]; then
cp -a "$BACKUP/90-web-capacity.conf" /etc/sysctl.d/
fi
sysctl -w net.core.somaxconn="$(cat "$BACKUP/somaxconn.before")"
该回滚会恢复整个 Nginx 服务覆盖目录,因此维护期间不要混入其他独立变更。
最后检查并恢复服务:
nginx -t
php-fpm8.3 -t
systemctl daemon-reload
systemctl restart php8.3-fpm
systemctl restart nginx
systemctl is-active nginx php8.3-fpm
回滚验收应再次确认首页、登录、动态接口和静态资源均正常,服务限制与连接参数已恢复,错误日志没有新增异常。缓存文件可以暂时保留,不必立即删除;若要清理,应先停用对应缓存配置,确认目录确实专用于本次缓存后再安排操作。



