个性化阅读
专注于IT技术分析

优化PHP-FPM以实现高性能

本文概述

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

但是, 它并不是以其高性能而著称, 特别是在高度并发的系统中。这就是为什么在这样的特殊用例中, 诸如Node(是的, 我知道, 这不是一种语言), Go和Elixir之类的语言正在接管的原因。

也就是说, 你可以采取很多措施来改善服务器上的PHP性能。本文侧重于php-fpm方面, 如果你使用的是Nginx, 这是在服务器上进行配置的自然方法。

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

什么是php-fpm?

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

优化PHP-FPM以实现高性能2

在PHP的情况下, 请求-响应周期(图片来源:ProinerTech)

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

现在, Web服务器与PHP的连接方式已经发生了精确的演变, 如果我们要深入研究所有细节, 这篇文章的长度将激增。但粗略地说, 在Apache成为首选Web服务器的时期, PHP是服务器内部包含的模块。

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

在php-fpm中, 管理PHP的责任在于服务器内部的PHP程序。换句话说, 只要知道如何发送和接收数据, 网络服务器(在我们的例子中为Nginx)就不会在意PHP的位置及其加载方式。如果需要, 在这种情况下, 你可以将PHP视为本身的另一台服务器, 它管理传入请求的某些子PHP进程(因此, 我们将请求到达服务器, 该请求由服务器接收并传递到服务器— —太疯狂了!

如果你已经完成了Nginx的任何设置, 甚至只是将它们撬了进去, 就会遇到以下类似情况:

location ~ \.php$ {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass unix:/run/php/php7.2-fpm.sock;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }

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

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

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

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

或以图形方式:

优化PHP-FPM以实现高性能4

PHP和Nginx如何一起工作(图片来源:DataDog)

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

PHP中的” FPM”部分代表” Fast Process Manager”, 这只是一种很好的说法, 它表示在服务器上运行的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 Configuration ;
;;;;;;;;;;;;;;;;;;;;;

; All relative paths in this configuration file are relative to PHP's install
; prefix (/usr). This prefix can be dynamically changed by using the
; '-p' argument from the command line.

;;;;;;;;;;;;;;;;;;
; Global Options ;
;;;;;;;;;;;;;;;;;;

[global]
; Pid file
; Note: the default prefix is /var
; Default Value: none
pid = /run/php/php7.2-fpm.pid

; Error log file
; If it's set to "syslog", log is sent to syslogd instead of being written
; into a local file.
; Note: the default prefix is /var
; Default Value: log/php-fpm.log
error_log = /var/log/php7.2-fpm.log

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

我们还看到/var/log/php7.2-fpm.log是php-fpm存储其日志的位置。

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

emergency_restart_threshold 10
emergency_restart_interval 1m
process_control_timeout 10s

前两个设置是谨慎的, 它们告诉php-fpm进程, 如果一分钟内有十个子进程失败, 则主php-fpm进程应自行重启。

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

第三个选项process_control_timeout告诉子进程在执行从父进程接收到的信号之前要等待这么长时间。例如, 当父进程发送KILL信号时, 如果子进程位于某个中间, 则此功能很有用。只需十秒钟, 他们就有更大的机会完成任务并优雅地退出。

令人惊讶的是, 这不是php-fpm配置的重点!这是因为php-fpm为处理网络请求而创建了一个新的进程池, 该池将具有单独的配置。在我的情况下, 池名称竟然是www, 而我要编辑的文件是/etc/php/7.2/fpm/pool.d/www.conf。

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

; Start a new pool named 'www'.
; the variable $pool can be used in any directive and will be replaced by the
; pool name ('www' here)
[www]

; Per pool prefix
; It only applies on the following directives:
; - 'access.log'
; - 'slowlog'
; - 'listen' (unixsocket)
; - 'chroot'
; - 'chdir'
; - 'php_values'
; - 'php_admin_values'
; When not set, the global prefix (or /usr) applies instead.
; Note: This directive can also be relative to the global prefix.
; Default Value: none
;prefix = /path/to/pools/$pool

; Unix user/group of processes
; Note: The user is mandatory. If the group is not set, the default user's group
;       will be used.
user = www-data
group = www-data

快速浏览以上代码片段的结尾, 可以解决为什么服务器进程以www-data的形式运行的困惑。如果在设置网站时遇到文件许可问题, 则可能已将目录的所有者或组更改为www-data, 从而使PHP进程能够写入日志文件和上传文档等。 。

最后, 我们得出问题的根源, 即流程管理器(pm)设置。通常, 你会看到默认值, 如下所示:

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

那么, “动态”在这里是什么意思?我认为官方文档可以最好地解释这一点(我的意思是, 该文件应该已经在你正在编辑的文件中, 但是为了防止万一没有被复制, 请在此处进行复制):

; Choose how the process manager will control the number of child processes.
; Possible Values:
;   static  - a fixed number (pm.max_children) of child processes;
;   dynamic - the number of child processes are set dynamically based on the
;             following directives. With this process management, there will be
;             always at least 1 children.
;             pm.max_children      - the maximum number of children that can
;                                    be alive at the same time.
;             pm.start_servers     - the number of children created on startup.
;             pm.min_spare_servers - the minimum number of children in 'idle'
;                                    state (waiting to process). If the number
;                                    of 'idle' processes is less than this
;                                    number then some children will be created.
;             pm.max_spare_servers - the maximum number of children in 'idle'
;                                    state (waiting to process). If the number
;                                    of 'idle' processes is greater than this
;                                    number then some children will be killed.
;  ondemand - no children are created at startup. Children will be forked when
;             new requests will connect. The following parameter are used:
;             pm.max_children           - the maximum number of children that
;                                         can be alive at the same time.
;             pm.process_idle_timeout   - The number of seconds after which
;                                         an idle process will be killed.
; Note: This value is mandatory.

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

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

那么, 这些设置有什么关系呢?

简而言之, 如果你的网站访问量很少, 那么”动态”设置通常会浪费资源。假设你将pm.min_spare_servers设置为3, 即使网站上没有流量, 也将创建并维护三个PHP进程。在这种情况下, “按需”是一个更好的选择, 让系统决定何时启动新流程。

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

使用pm = static可以固定子进程的数量, 从而可以将最大的系统资源用于服务请求而不是管理PHP。如果你确实走这条路, 请注意它有其指导原则和陷阱。关于它的一篇相当密集但非常有用的文章在这里。

最后的话

由于有关网络性能的文章可能会引发战争或使人们感到困惑, 因此在结束本文之前, 我觉得需要讲几句话。性能调优既涉及系统知识, 也涉及猜测和技巧。

即使你完全了解所有php-fpm设置, 也无法保证成功。如果你对php-fpm的存在一无所知, 则无需浪费时间担心它。继续做你已经在做的事情并继续。

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

如果你还没有准备好花时间优化PHP服务器, 那么你可以考虑利用诸如Kinsta之类的可靠平台来处理性能优化和安全性。

赞(1)
未经允许不得转载:srcmini » 优化PHP-FPM以实现高性能

评论 抢沙发

评论前必须登录!