优化PHP-FPM以实现高性能

本文概述

  • 什么是php-fpm?
  • 为什么要优化php-fpm?
  • 如何优化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以实现高性能

文章图片
在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_paramSCRIPT_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以实现高性能

文章图片
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: nonepid = /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.logerror_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 10emergency_restart_interval 1mprocess_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-datagroup = www-data

快速浏览以上代码片段的结尾, 可以解决为什么服务器进程以www-data的形式运行的困惑。如果在设置网站时遇到文件许可问题, 则可能已将目录的所有者或组更改为www-data, 从而使PHP进程能够写入日志文件和上传文档等。 。
最后, 我们得出问题的根源, 即流程管理器(pm)设置。通常, 你会看到默认值, 如下所示:
pm = dynamicpm.max_children = 5pm.start_servers = 3pm.min_spare_servers = 2pm.max_spare_servers = 4pm.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-FPM以实现高性能】如果你还没有准备好花时间优化PHP服务器, 那么你可以考虑利用诸如Kinsta之类的可靠平台来处理性能优化和安全性。

    推荐阅读