优化 PHP-FPM 以获得高性能

PHP 无处不在,并且可以说是 Internet Web 上部署最广泛的语言。

然而,它并不以其高性能而闻名,尤其是在高并发系统方面。这就是为什么对于此类专门的用例,Node(是的,我知道,它不是一种语言)、Go和 Elixir 等语言正在取代。

也就是说,您可以采取很多措施来提高服务器上的PHP 性能。本文重点关注php-fpm事情的侧面,如果您使用 Nginx,这是在服务器上配置的自然方法。

如果您知道php-fpm是什么,请随时跳转到优化部分。

什么是 PHP-fpm?

没有多少开发人员对DevOps方面感兴趣,即使在那些感兴趣的开发人员中,也很少有人知道幕后发生了什么。有趣的是,当浏览器向运行 PHP 的服务器发送请求时,形成第一个接触点的并不是 PHP;而是 PHP。相反,它是 HTTP 服务器,其中主要的是 Apache 和 Nginx。然后,这些“Web 服务器”必须决定如何连接到 PHP,并将请求类型、数据和标头传递给它。

请求-响应-in-php-e1542398057451
PHP 的请求-响应周期(图片来源:ProinerTech)

在现代 PHP 应用程序中,上面的“查找文件”部分是index.php,服务器配置为将所有请求委托给它。

现在,网络服务器连接到 PHP 的具体方式已经发生了变化,如果我们要深入了解所有细节,那么这篇文章的长度将会爆炸。但粗略地说,在 Apache 作为首选 Web 服务器占据主导地位的时期,PHP 是服务器内部包含的一个模块。

因此,每当收到请求时,服务器就会启动一个新进程,该进程将自动包含 PHP,并执行它。这种方法被称为mod_php“PHP as a module”的缩写。这种方法有其局限性,Nginx 克服了这一点php-fpm

php-fpm管理 PHP 的过程中,进程由服务器内的 PHP 程序负责。换句话说,网络服务器(在我们的例子中是 Nginx)并不关心 PHP 在哪里以及它是如何加载的,只要它知道如何从中发送和接收数据即可。如果您愿意,您可以将这种情况下的 PHP 本身视为另一台服务器,它管理传入请求的一些 PHP 子进程(因此,我们让请求到达服务器,该请求被服务器接收并传递到服务器) – 太疯狂了!:-P)。

如果你已经完成了 Nginx 设置,或者只是深入了解它们,你会遇到类似这样的情况:

     位置 ~ .php$ {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+.php)(/.+)$;
        fastcgi_pass unix:/run/php/php7.2-fpm.sock;
        fastcgi_index索引.php;
        包括 fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }

我们感兴趣的行是:fastcgi_pass unix:/run/php/php7.2-fpm.sock;,它告诉 Nginx 通过名为 的套接字与 PHP 进程通信php7.2-fpm.sock。因此,对于每个传入请求,Nginx 都会通过此文件写入数据,并在收到输出后将其发送回浏览器。

我必须再次强调,这并不是最完整或最准确的描述,但它对于大多数 DevOps 任务来说是完全准确的。

除此之外,让我们回顾一下到目前为止我们所学到的知识:

  • PHP不直接接收浏览器发送的请求。像 Nginx 这样的 Web 服务器首先拦截这些。
  • Web 服务器知道如何连接到 PHP 进程,并将所有请求数据(实际上是粘贴所有内容)传递给 PHP。
  • 当 PHP 完成其职责时,它将响应发送回 Web 服务器,Web 服务器将其发送回客户端(或大多数情况下是浏览器)。

或者以图形方式:

php 和 nginx
PHP 和 Nginx 如何协同工作(图片来源:DataDog)

到目前为止还不错,但现在出现了一个价值百万美元的问题:PHP-FPM 到底是什么?

PHP 中的“FPM”部分代表“快速进程管理器”,这只是一种奇特的说法,表示服务器上运行的 PHP 不是单个进程,而是生成、控制和终止的一些 PHP 进程由该 FPM 流程管理器关闭。Web 服务器将请求传递给此进程管理器。

PHP-FPM 本身就是一个完整的兔子洞,所以如果您愿意,请随意探索,但出于我们的目的,这么多解释就足够了。🙂

为什么要优化 PHP-fpm?

那么,当事情进展顺利时,为什么要担心所有这些舞蹈呢?为什么不让事情保持原样呢?

讽刺的是,这正是我针对大多数用例给出的建议。如果您的设置工作正常并且没有特殊用例,请使用默认值。但是,如果您希望扩展到单台机器之外的规模,那么从一台机器中挤出最大容量是至关重要的,因为它可以将服务器费用减少一半(甚至更多!)。

另一件需要意识到的事情是,Nginx 是为处理巨大的工作负载而构建的。它能够同时处理数千个连接,但如果您的 PHP 设置不是这样,您只会浪费资源,因为 Nginx 必须等待 PHP 完成当前进程并接受接下来,最终否定 Nginx 旨在提供的任何优势!

因此,抛开这一点,让我们看看在尝试优化时我们到底要改变什么php-fpm

如何优化PHP-FPM?

服务器上的配置文件位置php-fpm 可能有所不同,因此您需要进行一些研究才能找到它。如果在 UNIX 上,您可以使用find 命令。在我的 Ubuntu 上,路径是/etc/php/7.2/fpm/php-fpm.conf. 当然,7.2 是我正在运行的 PHP 版本。

该文件的前几行如下所示:

;;;;;;;;;;;;;;;;;;;;;;
; FPM 配置;
;;;;;;;;;;;;;;;;;;;;;;

; 该配置文件中的所有相对路径都是相对于 PHP 的安装
; 前缀(/usr)。该前缀可以通过使用动态更改
; 来自命令行的“-p”参数。

;;;;;;;;;;;;;;;;;;
; 全局选项;
;;;;;;;;;;;;;;;;;;

[全球的]
; PID文件
; 注意:默认前缀是/var
; 默认值:无
pid = /run/php/php7.2-fpm.pid

; 错误日志文件
; 如果设置为“syslog”,则日志将发送到 syslogd 而不是写入
; 到本地文件中。
; 注意:默认前缀是/var
; 默认值:log/php-fpm.log
error_log = /var/log/php7.2-fpm.log

有几件事应该是显而易见的:该行pid = /run/php/php7.2-fpm.pid告诉我们哪个文件包含进程的进程 ID php-fpm 。

我们还看到这就是 存储日志的/var/log/php7.2-fpm.log地方。php-fpm

在此文件中,再添加三个变量,如下所示:

紧急重启阈值 10
紧急重启间隔 1m
process_control_timeout 10s

前两个设置是警告性的,告诉php-fpm进程如果一分钟内有十个子进程失败,主php-fpm 进程应该重新启动。

这听起来可能不太健壮,但 PHP 是一个短暂的进程,确实会泄漏内存,因此在严重故障的情况下重新启动主进程可以解决很多问题。

第三个选项process_control_timeout告诉子进程在执行从父进程接收到的信号之前等待这么长时间。例如,当父进程发送 KILL 信号时,子进程正在进行某些操作,这非常有用。有了十秒钟的时间,他们将有更好的机会完成任务并优雅地退出。

令人惊讶的是,这不是配置的核心php-fpm!这是因为为了服务 Web 请求,php-fpm 创建了一个新的进程池,该进程池将具有单独的配置。就我而言,池名称是www ,我要编辑的文件是 /etc/php/7.2/fpm/pool.d/www.conf.

让我们看看这个文件的开头是什么:

; 启动一个名为“www”的新池。
; 变量 $pool 可以在任何指令中使用,并将被替换为
; 池名称(此处为“www”)
[万维网]

; 每个池前缀
; 它仅适用于以下指令:
; - '访问.log'
; - '慢日志'
; -“听”(unixsocket)
; - 'chroot'
; - 'chdir'
; - 'php_values'
; - 'php_admin_values'
; 如果未设置,则应用全局前缀(或 /usr)。
; 注意:该指令也可以相对于全局前缀。
; 默认值:无
;前缀 = /path/to/pools/$pool

; Unix 用户/进程组
; 注:用户为必填项。如果未设置组,则默认用户组
; 将会被使用。
用户 = www-数据
组 = www-数据

快速浏览一下上面代码片段的末尾就可以解决为什么服务器进程以www-data. 如果您在设置网站时遇到文件权限问题,您可能已将目录的所有者或组更改为www-data,从而允许 PHP 进程能够写入日志文件和上传文档等。

最后,我们找到问题的根源,即流程管理器 (pm) 设置。一般来说,您会看到类似这样的默认值:

pm = 动态
pm.max_children = 5
pm.start_servers = 3
pm.min_spare_servers = 2
pm.max_spare_servers = 4
pm.max_requests = 200

那么,这里的“动态”是什么意思呢?我认为官方文档最好地解释了这一点(我的意思是,这应该已经是您正在编辑的文件的一部分,但我在这里复制了它,以防万一它不是):

; 选择进程管理器如何控制子进程的数量。
; 可能的值:
; static - 固定数量 (pm.max_children) 的子进程;
; 动态 - 子进程的数量根据
; 遵循指令。通过这种流程管理,将有
; 始终至少有 1 个孩子。
; pm.max_children - 可以的最大子节点数
; 同时活着。
; pm.start_servers - 启动时创建的子项数量。
; pm.min_spare_servers - 'idle' 中子进程的最小数量
; 状态(等待处理)。如果数量
; “空闲”进程的数量少于此
; number 然后将创建一些子项。
; pm.max_spare_servers - 'idle' 中子进程的最大数量
; 状态(等待处理)。如果数量
; “空闲”进程的数量大于此
; 那么一些孩子就会被杀死。
; ondemand - 启动时不会创建子项。当以下情况时,孩子们将被分叉:
; 新请求将连接。使用以下参数:
; pm.max_children - 最大子节点数
; 可以同时活着。
; pm.process_idle_timeout - 秒数
; 空闲进程将被杀死。
; 注意:该值是强制性的。

因此,我们看到存在三个可能的值:

  • 静态:无论如何都会维护固定数量的 PHP 进程。
  • 动态php-fpm:我们可以指定 在任何给定时间点保持活动状态的最小和最大进程数。
  • ondemand:进程是按需创建和销毁的。

那么,这些设置有何作用呢?

简单来说,如果您的网站流量较低,“动态”设置大多数时候都是资源浪费。假设您pm.min_spare_servers 设置为 3,即使网站没有流量,也会创建并维护三个 PHP 进程。在这种情况下,“按需”是更好的选择,让系统决定何时启动新进程。

另一方面,处理大量流量或必须快速响应的网站将在这种情况下受到惩罚。创建新的 PHP 进程、使其成为池的一部分并对其进行监视是最好避免的额外开销。

使用pm = static 修复子进程的数量,让最大的系统资源用于服务请求而不是管理 PHP。如果您确实走这条路,请注意它有其指导方针和陷阱。这里有一篇关于它的相当密集但非常有用的文章。

最后的话

由于有关 Web 性能的文章可能会引发战争或令人困惑,因此我认为在结束本文之前应该说几句话。性能调优既是系统知识,也是猜测和黑暗艺术。

即使您熟记所有php-fpm 设置,也不能保证成功。如果您不知道 的存在php-fpm,那么您无需浪费时间担心它。继续做你已经在做的事情并继续下去。

同时,避免成为性能迷。是的,您可以通过从头开始重新编译 PHP 并删除所有不会使用的模块来获得更好的性能,但这种方法在生产环境中不够明智。优化某些东西的整个想法是看看您的需求是否与默认值不同(他们很少这样做!),并根据需要进行微小的更改。

如果您还没有准备好花时间优化 PHP 服务器,那么您可以考虑利用像Kinsta这样负责性能优化和安全性的可靠平台。

新媒体运营,新媒体代运营,cloudneo

原创文章,作者:超哥,如若转载,请注明出处:https://www.chaoneo.cn/archives/3368.html

0 0 投票
五星评级

如果您觉得超哥分享对您有所帮助的话,记得打赏给我😀

订阅
提醒
guest
0 评论
内联反馈
查看所有评论
QQ客服
加我微信
电话联系
电子邮件
我们将24小时内回复。
取消
0
喜欢你的想法,请评论x
()
x