Skip to content

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.confpool.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

bash
# 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 全局配置

ini
; ============== 全局配置 ==============

; 错误日志
[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 最核心的配置单元。每个进程池拥有独立的配置:

ini
; ============== 进程池配置示例 (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

验证安装

服务管理

bash
# 启动 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 运行

bash
# 检查 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 中配置状态页面访问:

nginx
location /fpm-status {
    allow 127.0.0.1;
    deny all;
    fastcgi_pass unix:/run/php/php8.2-fpm.sock;
}
bash
# 访问状态页面
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
# ============== 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;
    }
}

多进程池配置

通过配置多个进程池,可以实现资源隔离和差异化服务:

ini
; ============== 主站进程池 ==============
[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 配置:

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 SocketTCP 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 的慢日志是排查性能问题的重要工具:

ini
; 在进程池配置中开启慢日志
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
<?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 发挥最佳性能,需要调整操作系统的内核参数:

bash
# /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
bash
# 应用内核参数
sudo sysctl -p

# 增加文件描述符限制
# /etc/security/limits.conf
* soft nofile 65535
* hard nofile 65535

注意事项

常见错误排查

错误信息原因解决方案
Connection refusedFPM 未启动或端口不匹配检查 systemctl status php-fpm
Permission denied on socketNginx 用户无权访问 socket配置 listen.owner/listen.mode
502 Bad GatewayFPM 崩溃或超时检查 FPM 错误日志,调整超时时间
upstream sent too big header响应头过大调整 fastcgi_buffer_size
Primary script unknownSCRIPT_FILENAME 配置错误确认 root 路径正确
Worker 内存持续增长应用存在内存泄漏设置 pm.max_requests 定期重启

内存泄漏预防

ini
; pm.max_requests 是防止内存泄漏的关键配置
; 每个 worker 处理指定数量的请求后被 Master 进程回收并重新创建

pm.max_requests = 500

; 对于存在已知内存泄漏的应用,可以降低此值
; pm.max_requests = 100

; 值太大会增加内存泄漏风险,太小会增加 CPU 开销
; 一般建议 200~1000 之间

最佳实践

  1. 使用 Unix Socket:在 Nginx 和 PHP-FPM 同机部署时优先使用 Unix Socket,性能优于 TCP Socket。

  2. 合理计算 max_children:根据服务器可用内存和每个 worker 的平均内存占用计算,避免因 worker 过多导致 OOM(Out of Memory)。

  3. 设置 pm.max_requests:始终设置此值以预防内存泄漏。建议 200~1000 之间,根据应用特点调整。

  4. 开启慢日志:生产环境必须开启慢日志,设置合理的超时阈值(如 3~5 秒),定期分析慢日志找出性能瓶颈。

  5. 使用平滑重启:代码更新后使用 systemctl reload php-fpm 进行平滑重启,避免中断当前正在处理的请求。

  6. 配置 status_path:开启 FPM 状态页面(限制为仅本地访问),配合监控工具实现进程池的实时监控。

  7. 日志轮转:配置 logrotate 对 FPM 日志进行轮转,避免日志文件过大占用磁盘空间:

bash
# /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 的配置与调优是生产环境部署的关键环节。接下来你可以学习:

  • PECL 扩展安装 — 学习如何通过 PECL 安装 Redis、MongoDB 等扩展
  • PIE 简介 — 了解 PHP Installation Extension 这一新兴工具

参考链接