跳过内容

第 3 部分。应用程序服务器

PHP 和 PHP-FPM

本章将介绍 PHP 和 PHP-FPM。

PHP (PHP Hypertext Preprocessor) 是一种专门为 Web 应用程序开发而设计的源代码脚本语言。在 2024 年,PHP 占全球生成的网页的 80% 左右。PHP 是开源的,是许多著名 CMS(WordPress、Drupal、Joomla!、Magento 等)的核心。

PHP-FPM (FastCGI Process Manager) 自 PHP 5.3.3 版本起集成到 PHP 中。PHP 的 FastCGI 版本带来了额外的功能。


目标:您将学习如何

✔ 安装 PHP 应用服务器
✔ 配置 PHP-FPM 池
✔ 优化 PHP-FPM 应用服务器

🏁 PHPPHP-FPM应用服务器

知识⭐ ⭐ ⭐
复杂度⭐ ⭐ ⭐

阅读时间:30 分钟


概论

CGI (Common Gateway Interface) 和 FastCGI 允许 Web 服务器(Apache 或 Nginx)与开发语言(PHP、Python、Java)之间的通信

  • CGI 的情况下,每个请求都会创建一个 新进程,这在性能上效率较低。
  • FastCGI 依赖于 一定数量的进程 来处理其客户端请求。

PHP-FPM 除了 更高的性能 之外,还带来了

  • 更好地 划分应用程序 的可能性:启动具有不同 uid/gid 的进程,并使用个性化的 php.ini 文件,
  • 统计管理,
  • 日志管理,
  • 进程的动态管理和在不中断服务的情况下重启(“优雅”)。

注意

由于 Apache 有一个 PHP 模块,因此 php-fpm 通常在 Nginx 服务器上使用。

选择 PHP 版本

Rocky Linux 与其上游一样,提供许多语言版本。其中一些已达到生命周期结束,但为了继续托管尚未与新版本 PHP 兼容的旧应用程序而保留。请参阅 php.net 网站上的 支持版本 页面以选择支持的版本。

要获取可用版本的列表,请输入以下命令

$ sudo dnf module list php

Rocky Linux 9 - AppStream
Name                                                 Stream                                                  Profiles                                                                   Summary
php                                                  8.1 [d]                                                 common [d], devel, minimal

Hint: [d]efault, [e]nabled, [x]disabled, [i]nstalled

Remi 仓库提供了比 Appstream 仓库更新的 PHP 版本,包括 8.2 和 8.3 版本。

要安装 Remi 仓库,请运行以下命令

sudo dnf install https://rpms.remirepo.net/enterprise/remi-release-9.rpm

通过运行以下命令启用 Remi 仓库

sudo dnf config-manager --set-enabled remi

您现在可以通过输入以下命令激活更新的模块(PHP 8.3)

sudo dnf module enable php:remi-8.3
$ sudo dnf module list php

Rocky Linux 8 - AppStream
Name                                                 Stream                                                  Profiles                                                                   Summary
php                                                  7.2 [d]                                                 common [d], devel, minimal                                                 PHP scripting language
php                                                  7.3                                                     common [d], devel, minimal                                                 PHP scripting language
php                                                  7.4                                                     common [d], devel, minimal                                                 PHP scripting language
php                                                  8.0                                                     common [d], devel, minimal                                                 PHP scripting language

Hint: [d]efault, [e]nabled, [x]disabled, [i]nstalled

Rocky 从其 Appstream 仓库提供不同的 PHP 模块。

您会注意到 Rocky 8.9 的默认版本是 7.2,该版本在撰写本文时已达到生命周期结束。

您可以通过输入以下命令激活更新的模块

sudo dnf module enable php:8.0
==============================================================================================
Package               Architecture         Version               Repository             Size
==============================================================================================
Enabling module streams:
httpd                                      2.4
nginx                                      1.14
php                                        8.0

Transaction Summary
==============================================================================================

Is this ok [y/N]:

Transaction Summary
==============================================================================================

Is this ok [y/N]: y
Complete!

您现在可以继续安装 PHP 引擎。

安装 PHP CGI 模式

首先,安装并在 CGI 模式下使用 PHP。它只能与 Apache Web 服务器及其 mod_php 模块一起使用。本文档的 FastCGI 部分(php-fpm)解释了如何将 PHP 集成到 Nginx(以及 Apache)中。

PHP 的安装相对简单。它包括安装主包和您需要的几个模块。

以下示例安装带有通常与它一起安装的模块的 PHP。

sudo dnf install php php-cli php-gd php-curl php-zip php-mbstring

在安装过程中,系统将提示您导入 epel9(企业 Linux 9 扩展包)和 Remi 仓库的 GPG 密钥。输入 y 以导入密钥

Extra Packages for Enterprise Linux 9 - x86_64
Importing GPG key 0x3228467C:
Userid     : "Fedora (epel9) <epel@fedoraproject.org>"
Fingerprint: FF8A D134 4597 106E CE81 3B91 8A38 72BF 3228 467C
From       : /etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-9
Is this ok [y/N]: y
Key imported successfully
Remi's RPM repository for Enterprise Linux 9 - x86_64
Importing GPG key 0x478F8947:
Userid     : "Remi's RPM repository (https://rpms.remirepo.net/) <remi@remirepo.net>"
Fingerprint: B1AB F71E 14C9 D748 97E1 98A8 B195 27F1 478F 8947
From       : /etc/pki/rpm-gpg/RPM-GPG-KEY-remi.el9
Is this ok [y/N]: y
Key imported successfully
Running transaction check
Transaction check succeeded.
Running transaction test
Transaction test succeeded.

Complete!
sudo dnf install php php-cli php-gd php-curl php-zip php-mbstring

您可以检查安装的版本是否与预期版本一致。

$ php -v
PHP 8.3.2 (cli) (built: Jan 16 2024 13:46:41) (NTS gcc x86_64)
Copyright (c) The PHP Group
Zend Engine v4.3.2, Copyright (c) Zend Technologies
with Zend OPcache v8.3.2, Copyright (c), by Zend Technologies
$ php -v
PHP 7.4.19 (cli) (built: May  4 2021 11:06:37) ( NTS )
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies
with Zend OPcache v7.4.19, Copyright (c), by Zend Technologies

Apache 集成

要以 CGI 模式提供 PHP 页面,您必须安装 Apache 服务器,对其进行配置,激活它并启动它。

  • 安装
sudo dnf install httpd
activation:
sudo systemctl enable --now httpd
sudo systemctl status httpd
  • 不要忘记配置防火墙
sudo firewall-cmd --add-service=http --permanent
sudo firewall-cmd --reload

默认的 vhost 应该可以直接使用。PHP 提供了一个 phpinfo() 函数,该函数会生成一个有关其配置的摘要表。它对于测试 PHP 是否正常工作很有用。但是,请注意不要将此类测试文件留在您的服务器上。它们对您的基础设施构成了巨大的安全风险。

创建文件 /var/www/html/info.php/var/www/html 是默认 Apache 配置的默认 vhost 目录)

<?php
phpinfo();
?>

使用 Web 浏览器通过访问页面 http://your-server-ip/info.php 来检查服务器是否正常工作。

警告

不要将 info.php 文件留在您的服务器上!

安装 PHP cgi 模式 (PHP-FPM)

如前所述,将 Web 托管切换到 PHP-FPM 模式有很多优势。

安装只需要 php-fpm 包

sudo dnf install php-fpm

由于 php-fpm 从系统角度来看是一个服务,因此您必须激活并启动它。

sudo systemctl enable --now php-fpm
sudo systemctl status php-fpm

PHP cgi 模式的配置

主配置文件是 /etc/php-fpm.conf

include=/etc/php-fpm.d/*.conf
[global]
pid = /run/php-fpm/php-fpm.pid
error_log = /var/log/php-fpm/error.log
daemonize = yes

注意

php-fpm 配置文件有大量注释。去看看吧!

如您所见,/etc/php-fpm.d/ 目录中的带有 .conf 扩展名的文件始终会被包含在内。

默认情况下,名为 www 的 PHP 进程池声明位于 /etc/php-fpm.d/www.conf 中。

[www]
user = apache
group = apache

listen = /run/php-fpm/www.sock
listen.acl_users = apache,nginx
listen.allowed_clients = 127.0.0.1

pm = dynamic
pm.max_children = 50
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 35

slowlog = /var/log/php-fpm/www-slow.log

php_admin_value[error_log] = /var/log/php-fpm/www-error.log
php_admin_flag[log_errors] = on
php_value[session.save_handler] = files
php_value[session.save_path]    = /var/lib/php/session
php_value[soap.wsdl_cache_dir]  = /var/lib/php/wsdlcache
说明描述
[pool]进程池名称。配置文件可以包含多个进程池(池的名称用括号括起,表示新的部分的开始)。
listen定义使用的监听接口或 Unix 套接字。

配置访问 php-fpm 进程的方式

存在两种连接方式。

使用 inet-interface,例如

listen = 127.0.0.1:9000.

或者使用 UNIX 套接字

listen = /run/php-fpm/www.sock.

注意

当 Web 服务器和 PHP 服务器位于同一台机器上时,使用套接字会移除 TCP/IP 层,并优化性能。

使用接口时,您必须配置 listen.ownerlisten.grouplisten.mode 来指定 Unix 套接字的所有者、所有者组和权限。警告:两台服务器(Web 和 PHP)都必须对套接字具有访问权限。

使用套接字时,您必须配置 listen.allowed_clients 以将对 PHP 服务器的访问权限限制在某些 IP 地址上。

示例:listen.allowed_clients = 127.0.0.1

静态或动态配置

您可以静态或动态管理 PHP-FPM 进程。

在静态模式下,pm.max_children 设置子进程数量的限制。

pm = static
pm.max_children = 10

此配置以 10 个进程开始。

在动态模式下,PHP-FPM 最多启动由 pm.max_children 值指定数量的进程。它首先启动一些与 pm.start_servers 相对应的进程,至少保持 pm.min_spare_servers 值的不活动进程,最多保持 pm.max_spare_servers 值的不活动进程。

示例

pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3

PHP-FPM 将创建一个新进程来替换处理了相当于 pm.max_requests 的多个请求的进程。

默认情况下,pm.max_requests 的值为 0,这意味着进程永远不会被回收。pm.max_requests 选项对于存在内存泄漏的应用程序可能很有吸引力。

第三种操作模式是 ondemand 模式。此模式仅在收到请求时才启动进程。它不是流量很大的站点的最佳模式,仅保留用于特定需求(流量少的站点、管理后端等)。

注意

PHP-FPM 操作模式的配置对于确保您的 Web 服务器正常运行至关重要。

进程状态

与 Apache 及其 mod_status 模块类似,PHP-FPM 提供了一个页面,显示进程的状态。

要激活页面,请使用 pm.status_path 指令设置其访问路径。

pm.status_path = /status
$ curl http://localhost/status_php
pool:                 www
process manager:      dynamic
start time:           03/Dec/2021:14:00:00 +0100
start since:          600
accepted conn:        548
listen queue:         0
max listen queue:     15
listen queue len:     128
idle processes:       3
active processes:     3
total processes:      5
max active processes: 5
max children reached: 0
slow requests:        0

记录长时间请求

slowlog 指令指定接收长时间请求(例如,其时间超过 request_slowlog_timeout 指令的值)日志记录的日志文件。

生成的日志文件的默认位置是 /var/log/php-fpm/www-slow.log

request_slowlog_timeout = 5
slowlog = /var/log/php-fpm/www-slow.log

request_slowlog_timeout 为 0 表示禁用日志记录。

NGinx 集成

nginx 的默认设置已经包含了让 PHP 与 PHP-FPM 配合使用的必要配置。

配置文件 fastcgi.conf(或 fastcgi_params)位于 /etc/nginx/

fastcgi_param  SCRIPT_FILENAME    $document_root$fastcgi_script_name;
fastcgi_param  QUERY_STRING       $query_string;
fastcgi_param  REQUEST_METHOD     $request_method;
fastcgi_param  CONTENT_TYPE       $content_type;
fastcgi_param  CONTENT_LENGTH     $content_length;

fastcgi_param  SCRIPT_NAME        $fastcgi_script_name;
fastcgi_param  REQUEST_URI        $request_uri;
fastcgi_param  DOCUMENT_URI       $document_uri;
fastcgi_param  DOCUMENT_ROOT      $document_root;
fastcgi_param  SERVER_PROTOCOL    $server_protocol;
fastcgi_param  REQUEST_SCHEME     $scheme;
fastcgi_param  HTTPS              $https if_not_empty;

fastcgi_param  GATEWAY_INTERFACE  CGI/1.1;
fastcgi_param  SERVER_SOFTWARE    nginx/$nginx_version;

fastcgi_param  REMOTE_ADDR        $remote_addr;
fastcgi_param  REMOTE_PORT        $remote_port;
fastcgi_param  SERVER_ADDR        $server_addr;
fastcgi_param  SERVER_PORT        $server_port;
fastcgi_param  SERVER_NAME        $server_name;

# PHP only, required if PHP was built with --enable-force-cgi-redirect
fastcgi_param  REDIRECT_STATUS    200;

为了让 nginx 处理 .php 文件,请将以下指令添加到站点配置文件中

如果 PHP-FPM 正在监听端口 9000

location ~ \.php$ {
  include /etc/nginx/fastcgi_params;
  fastcgi_pass 127.0.0.1:9000;
}

如果 php-fpm 正在监听 UNIX 套接字

location ~ \.php$ {
  include /etc/nginx/fastcgi_params;
  fastcgi_pass unix:/run/php-fpm/www.sock;
}

Apache 集成

将 Apache 配置为使用 PHP 池非常简单。您必须使用代理模块以及 ProxyPassMatch 指令,例如

<VirtualHost *:80>
  ServerName web.rockylinux.org
  DocumentRoot "/var/www/html/current/public"

  <Directory "/var/www/html/current/public">
    AllowOverride All
    Options -Indexes +FollowSymLinks
    Require all granted
  </Directory>
  ProxyPassMatch ^/(.*\.php(/.*)?)$ "fcgi://127.0.0.1:9000/var/www/html/current/public"

</VirtualHost>

PHP 池的稳固配置¶a

优化服务请求的数量并分析 PHP 脚本使用的内存对于最大限度地提高启动线程的数量是必要的。

首先,您需要使用以下命令了解 PHP 进程使用的平均内存量。

这将为您提供一个关于此服务器上 PHP 进程平均内存占用量的相当准确的认识。

本文档的其余部分导致每个进程在满负荷时的内存占用量为 120MB。

在具有 8Gb RAM 的服务器上,为系统保留 1Gb,为 OPCache 保留 1Gb(请参阅本文档的其余部分),剩下 6Gb 用于处理来自客户端的 PHP 请求。

您可以得出结论,此服务器最多可以接受 50 个线程 ((6*1024) / 120)

针对此用例的 php-fpm 的示例配置是

其中

Opcache 配置

opcache(Optimizer Plus Cache)是您可以影响的第一个缓存级别。

它将已编译的 PHP 脚本保存在内存中,这会极大地影响 Web 页面的执行(删除对磁盘上脚本的读取 + 编译时间)。

要对其进行配置,您必须对以下内容进行操作

  • 根据命中率,为 opcache 分配的内存大小,对其进行正确配置
  • 要缓存的 PHP 脚本数量(键的数量 + 脚本的最大数量)
  • 要缓存的字符串数量

要安装它

sudo dnf install php-opcache

要对其进行配置,请编辑 /etc/php.d/10-opcache.ini 配置文件

opcache.memory_consumption=128
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=4000

其中

  • opcache.memory_consumption 对应于 opcache 所需的内存量(增加它直到获得正确的命中率)。
  • opcache.interned_strings_buffer 是要缓存的字符串数量。
  • opcache.max_accelerated_files 接近于 find ./ -iname "*.php"|wc -l 命令的结果。

要配置 opcache,请参考 info.php 页面(包括 phpinfo();)(例如,参见 Cached scriptsCached strings 的值)。

注意

在每次新部署新代码时,都需要清空 opcache(例如,通过重新启动 php-fpm 进程)。

注意

不要低估通过正确设置和配置 opcache 可以获得的速度提升。

作者:Antoine Le Morvan

贡献者:Steven Spencer、Ganna Zhyrnova