跳到内容

PHP 和 PHP-FPM

PHPPHP Hypertext Preprocessor)是一种专门为 Web 应用程序开发设计的源代码脚本语言。在 2024 年,PHP 占世界生成的网页的不到 80%。PHP 是开源的,也是最著名的 CMS(WordPress、Drupal、Joomla!、Magento 等)的核心。

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

概论

CGICommon 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.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 服务器的最佳运行至关重要。

进程状态

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 scriptsCached strings的值)。

注意

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

注意

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

作者:Antoine Le Morvan

贡献者:Steven Spencer、Ganna Zhyrnova、Joseph Brinkman