PHP 和 PHP-FPM¶
PHP (PHP Hypertext Preprocessor) 是一种专门为 Web 应用程序开发设计的源代码脚本语言。在 2024 年,PHP 占全球动态网页的比例接近 80%。PHP 是开源的,并且是大多数著名 CMS(WordPress、Drupal、Joomla!、Magento 等)的核心。
PHP-FPM (FastCGI Process Manager) 自 5.3.3 版本起集成到 PHP 中。PHP 的 FastCGI 版本带来了额外的功能。
概述¶
CGI (Common Gateway Interface) 和 FastCGI 允许 Web 服务器(Apache、Nginx 等)与开发语言(PHP、Python、Java)之间进行通信。
- 在 CGI 的情况下,每个请求都会创建一个 新进程,这在性能上效率较低。
- FastCGI 依赖于 一定数量的进程 来处理其客户端请求。
PHP-FPM 除了 更好的性能 外,还带来了
- 更好地 划分应用程序 的可能性:以不同的 uid/gid 启动进程,使用自定义的
php.ini
文件, - 统计信息的管理,
- 日志管理,
- 进程的动态管理和在不中断服务的情况下重启(“graceful”)。
注意
由于 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 仓库提供的 PHP 版本比 Appstream 仓库更新,包括 8.3 和 8.4 版本。
要安装 Remi 仓库,请运行以下命令(注意:如果您运行的是 Rocky Linux 8.x 或 10.x,请在下面的“release-”后面替换 8 或 10)
sudo dnf install https://rpms.remirepo.net/enterprise/remi-release-9.rpm
安装完 Remi 仓库后,运行以下命令启用它。
sudo dnf config-manager --set-enabled remi
现在,您可以通过输入以下命令来激活一个较新的模块(PHP 8.4)
sudo dnf module enable php:remi-8.4
$ 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(Extra Packages for Enterprise 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
- 激活
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-FPM (FastCGI)¶
正如我们在本文档前面所强调的,将 Web 托管切换到 PHP-FPM 模式有很多好处。
安装¶
安装仅限于 php-fpm 包
sudo dnf install php-fpm
由于 php-fpm 从系统角度来看是一个服务,因此必须激活并启动它。
sudo systemctl enable --now php-fpm
sudo systemctl status php-fpm
配置¶
主配置文件存储在 /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 接口,例如
listen = 127.0.0.1:9000
.
或者通过 Unix 套接字
listen = /run/php-fpm/www.sock
.
注意
当 Web 服务器和 PHP 服务器在同一台机器上时使用套接字可以移除 TCP/IP 层并优化性能。
通过接口工作时,您必须配置 listen.owner
、listen.group
、listen.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 服务器的最佳运行至关重要。
进程状态¶
PHP-FPM 像 Apache 及其 mod_status
模块一样,提供了一个显示进程状态的页面。
要激活页面,请通过 pm.status_path
指令设置其访问路径。
pm.status_path = /status
$ curl https:///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 进程池的稳健配置¶
优化能够处理的请求数量并分析 PHP 脚本使用的内存,以优化启动线程的最大数量至关重要。
首先,我们需要知道使用命令的 PHP 进程的平均内存使用量。
while true; do ps --no-headers -o "rss,cmd" -C php-fpm | grep "pool www" | awk '{ sum+=$1 } END { printf ("%d%s\n", sum/NR/1024,"Mb") }' >> avg_php_proc; sleep 60; done
一段时间后,这应该会让我们对该服务器上 PHP 进程的平均内存占用有一个相当准确的了解。
本文档其余部分的结果是,在满负荷运行时,每个进程的内存占用为 120MB。
在一台拥有 8GB RAM 的服务器上,保留 1GB 用于系统,1GB 用于 OPCache(请参阅本文档其余部分),剩下 6GB 用于处理客户端的 PHP 请求。
我们可以很容易地得出结论,该服务器最多可以接受 50 个线程 ((6*1024) / 120)
。
针对此用例的良好 php-fpm
配置将是
pm = dynamic
pm.max_children = 50
pm.start_servers = 12
pm.min_spare_servers = 12
pm.max_spare_servers = 36
pm.max_requests = 500
其中
pm.start_servers
=max_children
的 25%pm.min_spare_servers
=max_children
的 25%pm.max_spare_servers
=max_children
的 75%
Opcache 配置¶
opcache
(Optimizer Plus Cache) 是我们可以影响的第一级缓存。
它将编译后的 PHP 脚本保存在内存中,这极大地影响了网页的执行(消除了从磁盘读取脚本 + 编译时间)。
要配置它,我们必须处理
- 根据命中率分配给 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
命令的结果。
您可以参考 info.php
页面(包括 phpinfo();
)来配置 opcache(例如,查看 Cached scripts
和 Cached strings
的值)。
注意
每次部署新代码后,都需要清空 opcache(例如通过重启 php-fpm 进程)。
注意
不要低估正确设置和配置 opcache 所能带来的速度提升。
作者:Antoine Le Morvan
贡献者:Steven Spencer, Ganna Zhyrnova, Joseph Brinkman