第三部分. 应用服务器
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 的 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(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
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 接口
,例如
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 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
命令的结果。
要配置 opcache,请参考 info.php
页面(包含 phpinfo();
)(例如,查看 Cached scripts
和 Cached strings
的值)。
注意
每次新代码部署时,都需要清空 opcache(例如,通过重启 php-fpm 进程)。
注意
不要低估正确设置和配置 opcache 所能带来的速度提升。
作者:Antoine Le Morvan
贡献者:Steven Spencer, Ganna Zhyrnova