第 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 应用服务器
PHP、PHP-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.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 服务器正常运行至关重要。
进程状态¶
与 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 进程使用的平均内存量。
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,为 OPCache 保留 1Gb(请参阅本文档的其余部分),剩下 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 脚本保存在内存中,这会极大地影响 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 scripts
和 Cached strings
的值)。
注意
在每次新部署新代码时,都需要清空 opcache(例如,通过重新启动 php-fpm 进程)。
注意
不要低估通过正确设置和配置 opcache 可以获得的速度提升。
作者:Antoine Le Morvan
贡献者:Steven Spencer、Ganna Zhyrnova