FastCGI 进程管理器(FPM)
PHP-FPM(FastCGI Process Manager)是 PHP 官方推荐的 FastCGI 进程管理实现,作为 PHP 的一个 SAPI(Server Application Programming Interface),专门用于处理高并发 Web 请求。PHP-FPM 自 PHP 5.3 起成为官方组件,目前已成为生产环境中部署 PHP 应用的事实标准。本节将全面讲解 PHP-FPM 的安装、配置、进程管理和性能调优。
前置知识
- 了解 PHP 的基本安装(参考 Unix/macOS 安装)
- 熟悉 Nginx 或 Apache 的基本配置
- 了解 FastCGI 协议的基本概念
- 具备 Linux 系统管理基础(systemd 服务管理)
基础概念
什么是 PHP-FPM
PHP-FPM 是一个独立于 PHP 核心的进程管理器,其核心职责包括:
- 进程管理:管理 PHP worker 进程的创建、销毁和重启
- 请求处理:接收来自 Web 服务器(Nginx/Apache)的 FastCGI 请求并分发给 worker 进程
- 配置管理:通过
php-fpm.conf和pool.d/*.conf进行灵活配置 - 高级功能:支持平滑重启、慢日志记录、进程状态监控、优雅停止等
PHP-FPM 架构
┌──────────────┐
│ Nginx │
│ (Web Server)│
└──────┬───────┘
│ FastCGI 协议
┌──────┴───────┐
│ PHP-FPM │
│ (Master) │
│ - 监听连接 │
│ - 管理进程 │
└──────┬───────┘
┌────────────┼────────────┐
│ │ │
┌──────┴──┐ ┌────┴────┐ ┌───┴─────┐
│ Worker 1│ │ Worker 2│ │ Worker N│
│ 处理请求 │ │ 处理请求 │ │ 处理请求 │
└─────────┘ └─────────┘ └─────────┘PHP-FPM 采用 Master-Worker 架构:
- Master 进程:负责监听 FastCGI 连接、管理 worker 进程池、处理信号
- Worker 进程:实际执行 PHP 代码,处理用户请求
进程管理模式
PHP-FPM 提供三种进程管理模式:
| 模式 | 说明 | 适用场景 |
|---|---|---|
static | 启动时创建固定数量的 worker | 内存充裕、流量稳定的服务器 |
dynamic | 根据负载动态调整 worker 数量 | 流量波动较大的应用 |
ondemand | 按需创建 worker,空闲时销毁 | 低流量或开发环境 |
安装配置
安装 PHP-FPM
# Ubuntu/Debian
sudo apt install php8.2-fpm -y
# CentOS/RHEL (Remi 仓库)
sudo dnf install php82-php-fpm -y
# 或标准安装
sudo dnf install php-fpm -y
# macOS (Homebrew)
brew install php@8.2
# Homebrew 的 PHP 包已内置 FPM
# 从源码编译(需要在 configure 时启用)
./configure --enable-fpm --with-fpm-user=www-data --with-fpm-group=www-data
make && sudo make install核心配置文件结构
PHP-FPM 的配置文件分为两个层级:
/usr/local/php/etc/ # 编译安装路径
├── php-fpm.conf # 全局配置
├── php-fpm.d/
│ ├── www.conf # 默认进程池配置
│ └── api.conf # 自定义进程池配置
└── php.ini # PHP 运行时配置
# Ubuntu/Debian 路径
/etc/php/8.2/
├── fpm/
│ ├── php-fpm.conf # 全局配置
│ ├── conf.d/ # 额外配置
│ └── pool.d/
│ └── www.conf # 进程池配置
└── ...php-fpm.conf 全局配置
; ============== 全局配置 ==============
; 错误日志
[global]
error_log = /var/log/php-fpm/error.log
; 进程 PID 文件
pid = /var/run/php-fpm/php-fpm.pid
; 紧急重启超时(单位:秒)
; Master 进程异常退出后,worker 在此时间内仍可完成当前请求
emergency_restart_threshold = 10
emergency_restart_interval = 1m
process_control_timeout = 10s
; 运行日志级别:alert, error, warning, notice, debug
log_level = notice
; 是否以守护进程方式运行
daemonize = yes
; 包含进程池配置目录
include = /usr/local/php/etc/php-fpm.d/*.conf
; include = /etc/php/8.2/fpm/pool.d/*.conf (Ubuntu/Debian)进程池配置(pool.d/*.conf)
进程池是 PHP-FPM 最核心的配置单元。每个进程池拥有独立的配置:
; ============== 进程池配置示例 (www.conf) ==============
; 进程池名称(用于日志和状态页面标识)
[www]
; 用户和用户组
user = www-data
group = www-data
; 监听方式
listen = /run/php/php8.2-fpm.sock
; 或使用 TCP 端口
; listen = 127.0.0.1:9000
; Socket 权限
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
; ============== 进程管理器配置 ==============
; 进程管理模式:static / dynamic / ondemand
pm = dynamic
; static 模式下固定的 worker 数量
; dynamic 模式下启动时的 worker 数量
pm.max_children = 50
; dynamic 模式:启动时创建的 worker 数量
pm.start_servers = 5
; dynamic 模式:空闲时的最小 worker 数量
pm.min_spare_servers = 3
; dynamic 模式:空闲时的最大 worker 数量
pm.max_spare_servers = 20
; ondemand 模式:worker 空闲超时时间(秒)
; pm.process_idle_timeout = 10s
; 每个 worker 处理多少请求后重启(防止内存泄漏)
pm.max_requests = 500
; ============== 性能调优 ==============
; 每个 worker 的状态请求超时时间
request_terminate_timeout = 60s
; 单个请求内存限制(0 表示不限制)
; 设置后超出将触发 PHP 错误
; request_terminate_memory = 256M
; ============== 慢日志配置 ==============
; 慢日志超时时间(秒),0 表示关闭
request_slowlog_timeout = 5s
; 慢日志文件路径
slowlog = /var/log/php-fpm/slow.log
; ============== 状态页面 ==============
; FPM 状态页面(用于监控)
pm.status_path = /fpm-status
; ============== PHP 运行时配置覆盖 ==============
; 可以在此覆盖 php.ini 中的设置(仅对当前池生效)
php_admin_value[error_log] = /var/log/php-fpm/www-error.log
php_admin_flag[log_errors] = on
; 设置 PHP 值(不能被 ini_set 覆盖)
php_value[date.timezone] = Asia/Shanghai
php_value[session.save_handler] = redis
php_value[session.save_path] = tcp://127.0.0.1:6379
php_value[upload_max_filesize] = 64M
php_value[post_max_size] = 128M
; 设置 PHP 标志
php_flag[display_errors] = off
; 加载额外的 PHP 配置文件
php_admin_value[open_basedir] = /var/www/html:/tmp:/usr/share/php
; 设置环境变量
env[APP_ENV] = production
env[DB_HOST] = localhost
env[DB_PORT] = 3306
; ============== 进程优先级 ==============
; worker 进程的 nice 值(-19 到 20,值越小优先级越高)
; priority = -19
; ============== 访问控制 ==============
; 限制可以访问 FastCGI 的 IP
; listen.allowed_clients = 127.0.0.1
; 限制每个 worker 处理的连接数
; pm.max_connections = 1024验证安装
服务管理
# 启动 PHP-FPM
sudo systemctl start php8.2-fpm
# 设置开机自启
sudo systemctl enable php8.2-fpm
# 查看服务状态
sudo systemctl status php8.2-fpm
# 平滑重启(不中断当前请求)
sudo systemctl reload php8.2-fpm
# 完全重启
sudo systemctl restart php8.2-fpm
# 停止服务
sudo systemctl stop php8.2-fpm
# 检查配置文件语法
sudo php-fpm8.2 -t
# 输出:NOTICE: configuration file /etc/php/8.2/fpm/php-fpm.conf test is successful验证 FPM 运行
# 检查 FPM 进程
ps aux | grep php-fpm
# 应该能看到一个 master 进程和多个 worker 进程
# 检查监听端口/Socket
sudo ss -tlnp | grep php-fpm
# TCP: LISTEN 0 511 127.0.0.1:9000 ...
# Socket: LISTEN 0 511 /run/php/php8.2-fpm.sock ...
# 使用 cgi-fcgi 测试连接
cgi-fcgi -bind -connect /run/php/php8.2-fpm.sock
# 或
echo "<?php echo 'FPM OK'; ?>" | cgi-fcgi -bind -connect 127.0.0.1:9000状态页面验证
在 Nginx 中配置状态页面访问:
location /fpm-status {
allow 127.0.0.1;
deny all;
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
}# 访问状态页面
curl http://127.0.0.1/fpm-status
# 输出示例:
# pool: www
# process manager: dynamic
# start time: 01/Jan/2024:00:00:00 +0800
# start since: 12345
# accepted conn: 67890
# listen queue: 0
# max listen queue: 5
# listen queue len: 0
# idle processes: 8
# active processes: 2
# total processes: 10
# max active processes: 15
# max children reached: 0
# slow requests: 3详细配置
Nginx 整合配置
PHP-FPM 与 Nginx 的整合是生产环境最常见的部署方式:
# ============== Nginx 站点配置 ==============
# 上游 PHP-FPM 定义
upstream php-fpm {
# Unix Socket 方式(推荐,性能更好)
server unix:/run/php/php8.2-fpm.sock;
# TCP Socket 方式(适用于远程 FPM)
# server 127.0.0.1:9000;
# 可以定义多个 FPM 实例实现负载均衡
# server unix:/run/php/php8.2-fpm-api.sock;
}
server {
listen 80;
server_name example.com www.example.com;
root /var/www/html/public;
index index.php index.html;
charset utf-8;
# 安全相关 Header
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# 主路由
location / {
try_files $uri $uri/ /index.php?$query_string;
}
# PHP 文件处理
location ~ \.php$ {
try_files $uri =404;
fastcgi_pass php-fpm;
fastcgi_index index.php;
# 脚本文件路径
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT $document_root;
# FastCGI 标准参数
include fastcgi_params;
# 超时设置
fastcgi_connect_timeout 60s;
fastcgi_send_timeout 60s;
fastcgi_read_timeout 60s;
# 缓冲设置
fastcgi_buffer_size 128k;
fastcgi_buffers 4 256k;
fastcgi_busy_buffers_size 256k;
# 传递 HTTPS 相关信息
fastcgi_param HTTPS $https if_not_empty;
# 防止 Nginx 缓冲过大响应
fastcgi_max_temp_file_size 0;
# 传递真实 IP
fastcgi_param REMOTE_ADDR $remote_addr;
set $real_scheme $scheme;
fastcgi_param REQUEST_SCHEME $real_scheme;
}
# 静态文件直接由 Nginx 处理
location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|eot)$ {
expires 30d;
access_log off;
add_header Cache-Control "public, immutable";
try_files $uri =404;
}
# 禁止访问隐藏文件
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
# FPM 状态页面(限制访问)
location /fpm-status {
allow 127.0.0.1;
deny all;
fastcgi_pass php-fpm;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
# 健康检查
location /health {
access_log off;
return 200 "OK";
add_header Content-Type text/plain;
}
}多进程池配置
通过配置多个进程池,可以实现资源隔离和差异化服务:
; ============== 主站进程池 ==============
[www]
user = www-data
group = www-data
listen = /run/php/php8.2-fpm-www.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
pm = dynamic
pm.max_children = 50
pm.start_servers = 5
pm.min_spare_servers = 3
pm.max_spare_servers = 20
pm.max_requests = 500
; 限制 open_basedir
php_admin_value[open_basedir] = /var/www/html:/tmp:/usr/share/php
; ============== API 服务进程池(更高优先级) ==============
[api]
user = www-data
group = www-data
listen = /run/php/php8.2-fpm-api.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
pm = dynamic
pm.max_children = 30
pm.start_servers = 3
pm.min_spare_servers = 2
pm.max_spare_servers = 10
pm.max_requests = 1000
; API 服务不需要 open_basedir 限制
; 但设置更严格的安全限制
php_admin_value[open_basedir] = /var/www/api:/tmp
; 设置更高的超时
request_terminate_timeout = 120s
; ============== 后台任务进程池 ==============
[worker]
user = www-data
group = www-data
listen = /run/php/php8.2-fpm-worker.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
pm = ondemand
pm.max_children = 10
pm.max_requests = 200
; 后台任务可能有较长的执行时间
request_terminate_timeout = 300s
; 更高的内存限制
php_value[memory_limit] = 512M对应的 Nginx 配置:
# API 使用独立的 FPM 池
upstream php-fpm-www {
server unix:/run/php/php8.2-fpm-www.sock;
}
upstream php-fpm-api {
server unix:/run/php/php8.2-fpm-api.sock;
}
upstream php-fpm-worker {
server unix:/run/php/php8.2-fpm-worker.sock;
}
server {
listen 80;
server_name example.com;
root /var/www/html/public;
# 前端请求使用 www 池
location ~ \.php$ {
try_files $uri =404;
fastcgi_pass php-fpm-www;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
server {
listen 80;
server_name api.example.com;
root /var/www/api/public;
# API 请求使用 api 池
location ~ \.php$ {
try_files $uri =404;
fastcgi_pass php-fpm-api;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
fastcgi_read_timeout 120s;
}
}Socket 与 TCP 选择
| 对比项 | Unix Socket | TCP Socket |
|---|---|---|
| 性能 | 更快(无需 TCP 协议开销) | 略慢 |
| 适用场景 | Nginx 和 FPM 在同一台机器 | Nginx 和 FPM 分布在不同机器 |
| 权限管理 | 需要 listen.owner/mode | 通过防火墙控制 |
| 配置复杂度 | 需要处理文件权限 | 相对简单 |
选择建议
- Nginx 和 PHP-FPM 在同一台服务器上:使用 Unix Socket
- Nginx 和 PHP-FPM 分布在不同服务器上:使用 TCP Socket
- Unix Socket 出现权限问题时:可以临时切换为 TCP Socket 排查
慢日志分析
PHP-FPM 的慢日志是排查性能问题的重要工具:
; 在进程池配置中开启慢日志
request_slowlog_timeout = 5s
slowlog = /var/log/php-fpm/slow.log慢日志记录示例:
[15-Jan-2024 10:30:45] WARNING: [pool www] child 12345, script
/var/www/html/index.php - execution exceeded 5.001724 sec
[15-Jan-2024 10:30:40] ---> /var/www/html/Service/OrderService.php:156
[15-Jan-2024 10:30:42] ---> /var/www/html/Repository/OrderRepository.php:89
[15-Jan-2024 10:30:43] ---> /var/www/html/Model/Order.php:234实战示例
计算合适的 pm.max_children
根据服务器内存计算合理的 worker 数量:
<?php
declare(strict_types=1);
/**
* PHP-FPM Worker 数量计算器
*
* 公式:max_children = (总内存 - 系统预留 - 其他进程占用) / 每个 worker 内存
*/
$totalMemoryMb = 8192; // 服务器总内存(MB)
systemReservedMb = 1024; // 系统预留(MB)
otherProcessMb = 2048; // Nginx、MySQL 等其他进程占用(MB)
workerMemoryMb = 64; // 每个 PHP worker 平均内存(MB)
$availableMemory = $totalMemoryMb - $systemReservedMb - $otherProcessMb;
$maxChildren = (int) floor($availableMemory / $workerMemoryMb);
echo "服务器总内存: {$totalMemoryMb}MB\n";
echo "系统预留: {$systemReservedMb}MB\n";
echo "其他进程: {$otherProcessMb}MB\n";
echo "可用内存: {$availableMemory}MB\n";
echo "每 Worker 内存: {$workerMemoryMb}MB\n";
echo "---\n";
echo "建议 max_children: {$maxChildren}\n";
// dynamic 模式推荐配置
$startServers = (int) ceil($maxChildren * 0.1);
$minSpare = (int) ceil($maxChildren * 0.1);
$maxSpare = (int) ceil($maxChildren * 0.4);
echo "\ndynamic 模式推荐配置:\n";
echo "pm.start_servers = {$startServers}\n";
echo "pm.min_spare_servers = {$minSpare}\n";
echo "pm.max_spare_servers = {$maxSpare}\n";系统优化配置
为了让 PHP-FPM 发挥最佳性能,需要调整操作系统的内核参数:
# /etc/sysctl.conf
# 增加文件描述符限制
fs.file-max = 65535
# 减少 TCP 连接超时回收
net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_keepalive_time = 300
net.ipv4.tcp_tw_reuse = 1
# 增加本地端口范围
net.ipv4.ip_local_port_range = 1024 65535
# SOMAXCONN(Socket 连接队列大小)
net.core.somaxconn = 65535
# TCP 最大缓冲区大小
net.core.tcp_max_syn_backlog = 65535
net.core.netdev_max_backlog = 65535# 应用内核参数
sudo sysctl -p
# 增加文件描述符限制
# /etc/security/limits.conf
* soft nofile 65535
* hard nofile 65535注意事项
常见错误排查
| 错误信息 | 原因 | 解决方案 |
|---|---|---|
Connection refused | FPM 未启动或端口不匹配 | 检查 systemctl status php-fpm |
Permission denied on socket | Nginx 用户无权访问 socket | 配置 listen.owner/listen.mode |
502 Bad Gateway | FPM 崩溃或超时 | 检查 FPM 错误日志,调整超时时间 |
upstream sent too big header | 响应头过大 | 调整 fastcgi_buffer_size |
Primary script unknown | SCRIPT_FILENAME 配置错误 | 确认 root 路径正确 |
| Worker 内存持续增长 | 应用存在内存泄漏 | 设置 pm.max_requests 定期重启 |
内存泄漏预防
; pm.max_requests 是防止内存泄漏的关键配置
; 每个 worker 处理指定数量的请求后被 Master 进程回收并重新创建
pm.max_requests = 500
; 对于存在已知内存泄漏的应用,可以降低此值
; pm.max_requests = 100
; 值太大会增加内存泄漏风险,太小会增加 CPU 开销
; 一般建议 200~1000 之间最佳实践
使用 Unix Socket:在 Nginx 和 PHP-FPM 同机部署时优先使用 Unix Socket,性能优于 TCP Socket。
合理计算 max_children:根据服务器可用内存和每个 worker 的平均内存占用计算,避免因 worker 过多导致 OOM(Out of Memory)。
设置 pm.max_requests:始终设置此值以预防内存泄漏。建议 200~1000 之间,根据应用特点调整。
开启慢日志:生产环境必须开启慢日志,设置合理的超时阈值(如 3~5 秒),定期分析慢日志找出性能瓶颈。
使用平滑重启:代码更新后使用
systemctl reload php-fpm进行平滑重启,避免中断当前正在处理的请求。配置 status_path:开启 FPM 状态页面(限制为仅本地访问),配合监控工具实现进程池的实时监控。
日志轮转:配置
logrotate对 FPM 日志进行轮转,避免日志文件过大占用磁盘空间:
# /etc/logrotate.d/php-fpm
/var/log/php-fpm/*.log {
daily
missingok
rotate 14
compress
delaycompress
notifempty
create 0640 www-data adm
sharedscripts
postrotate
[ -f /var/run/php/php8.2-fpm.pid ] && kill -USR1 $(cat /var/run/php/php8.2-fpm.pid) 2>/dev/null || true
endscript
}下一节
PHP-FPM 的配置与调优是生产环境部署的关键环节。接下来你可以学习: