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 版本带来了额外的功能。
概论¶
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 存储库提供的 PHP 版本比 Appstream 存储库更新,包括 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)中,我们将了解如何在 Nginx(以及 Apache)中集成 PHP。
安装¶
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
- 激活
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 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 池的稳固配置¶
优化能够处理的请求数量并分析 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