人人都能看懂的全栈开发教程——PHP-FPM

人人都能看懂的全栈开发教程——PHP-FPM

Chris Yue No Comment
Posts

上一篇已经引入了 PHP-FPM 的使用方式,但聊得还不够深入,本篇将详细为大家讲解 PHP-FPM 更多的使用细节。

大家都知道,PHP 运行代码,是要消耗资源的,比如 CPU 和内存资源。如果我们的项目发布在了生产机上,因为生产机面对的是成千上万的访问量,我们就不能像在本地开发机上访问站点一样,不用操心资源不够用的问题。

做产品的一个好习惯是,对代码要处理的数据大小,要有控制的意识,以防止意外出现。从技术的角度,是对运行时内存的控制,这在 PHP 里是有相关的配置的。一般来说 PHP 的配置文件位于 /etc/php ,这里面有一个叫做 php.ini 的文件,便是本机 PHP 运行环境的默认配置文件(当然,不同的发行版可能文件路径有所不同,比如 Ubuntu 下还会根据 PHP 版本,以及是命令行下运行还是 FPM 下运行还会分更深的路径,具体的就不用多说了,大家都应该能猜得到如何找到相应的配置文件)。此文件里有一个选项,叫做 memory_limit 。虽然此选项可以设置为 -1 即『无限制』,但建议大家最好别这么做。

实际上团队也应当要求产品有控制处理数据大小的意识,这是做产品的一个好习惯,比如列表需求一定要有意识控制返回的最大数目,用户输入一定要求控制输入长度等。

回到技术的角度继续说。如果光是控制了单个进程的处理数据大小还不够,因为我们的项目将运行在公网上,为了让更多的请求能够快速被响应,我们应该尽量让可以运行的 PHP 进程足够多,但又不至于超过当前机器实际可分配给 PHP 的内存大小。

PHP-FPM 是 PHP FastCGI Process Manager 的缩写,即『PHP FastCGI 进程管理器』,作为管理器,上面提到的技术需求,就是由它来负责实现的。

/etc/php 目录下,还有一个 php-fpm.conf 文件,此文件是专门用来管理 PHP-FPM 服务的相关设置的,此文件目前我们感兴趣的就只有一个配置 process.max,此配置设置了 PHP-FPM 最大能同时处理多少个需要 PHP 处理的请求。另外,无论什么发行版,此文件最后基本上都需要引入另外一个目录里的所有文件:

include=/etc/.../php-fpm.d/*.conf

并且 php-fpm.d 目录下,往往有一个 www.conf 文件,大部分我们感兴趣的配置,都在这个文件里面。

首先,此文件定义了『池』名,即 pool。在开发中,一般来说,『池』意味着一个用于存储可以复用的资源的容器,这里的资源是一个比较抽象的概念,举例来说,比如数据库的链接,就是一个资源;而 PHP 进程也是。使用『池』来存储资源,基本上目的都是为了节省重新创建资源的时间。无论是数据库链接也好,还是 PHP 进程也好,这些资源在创建时,都是需要消耗时间的,但如果通过一个容器来将这些使用之后的资源保存起来,不释放其内存,就可以实现下一次直接复用了。

虽然默认只有一个 PHP 进程池,但实际上你还可以创建更多的进程池。

接下来我们感兴趣的配置是 user 和 group。这两个配置跟资源控制没有什么关系,但是一个很重要的选项,新手往往会在这两个选项上翻车,导致 PHP 的文件操作出现权限的问题。

我们经常会让 PHP 读写一些文件,最常见的就是写日志,如果 PHP 部署到线上之后,发现写不了日志报错,就需要重点检查这两个配置和相关路径或者文件的权限。我也见过一些心比较大的运维,遇到权限问题就直接把文件或者目录的权限设置为权限全开,这实际上是一种安全风险极大且不负责任的做法。一个合格的运维应当要清楚文件和目录的权限分配是怎样的。

listen 也是我们需要关注的一个选项。虽然跟资源分配也没什么关系,但此处的定义必须和 NGINX 的配置保持一致。一般来 NGINX 直接返回 Bad Gateway 错误,很可能跟此处配置有关。一般来说 www.conf 提供的示例是一个路径,实际上应该叫做 unix socket,目前可以先不用关心 unix socket 和端口的区别,现在只用知道,unix socket 不走网络,所以理论上比端口传输数据的效率更高一点点,但也因为无法支持网络,所以只能支持本机的不同服务相互传输数据。

pm 则是跟资源分配有关系的了。此选项定义了三种生成 PHP 处理进程的方式,static 是静态方式,即设置好运行 FPM 时会开启多少个 PHP 进程,之后进程数就不会再变了。ondemand 则稍微灵活一点,FPM 刚启动是没有进程的,有请求需要处理才会创建进程,同时需要处理的请求越多,则创建得越多。如果使用这种模式,则还有两个选项需要设置,一是最多同时处理多少请求,二是如果有进程闲置,多长时间之后释放这个进程的资源。

还有一种模式叫 dynamic ,可以理解为 ondemand 的加强版。你可以定义当 FPM 一开始的时候就创建多少个进程,最少保留多少个闲置的进程,最多保留多少个闲置的进程。需要注意的是,FPM 运行之后创建多少进程必须定义在最大闲置和最小闲置进程数之间。

上述三种方式,最好理解,最简单的就是第一种。简单不一定意味着不实用。如果你本来对内存分配很明确,那么第一种就是最好的选择,比如,设置 PHP 每个进程最大处理 128M 的数据,你打算这台机器同时也就处理 10 个请求,那么你就可以设置 FPM 的 pmstatic ,并设置 pm.max_children10,然后 FPM 一来就分配 1280M 内存,不会变多也不会变少。

如果你的机器内存比较紧张,但对响应速度没啥要求,则可以使用 ondemand 方式。当你这台机器闲置的时候,FPM 释放的内存可以被系统拿去做点别的事情。

当然,你想让系统可以偶尔将内存挪为它用的同时,还想保证响应的速度,那么 dynamic 则是更好的选择。dynamicondemand 最大的不同是:如果并发涨起来了之后,闲置的进程已经小于了设置的最小闲置进程,那么 FPM 会立马创建新的进程,做好让后面更多的请求能得到尽快响应的准备。

聪明的你可能也已经能想到,尽量让 max_children 的值乘以 memory_limit 再加上系统占用内存,不要超过实际物理内存,否则就会遇到内存不够用的错误发生;或者被系统强行使用 swap 来存储数据,但 swap 就是硬盘的访问速度,也快不起来。

另外还有两个值得一提的配置,一个是 pm.max_request,这个值定义了一个进程在处理多少个请求之后,就必须销毁重新创建。在以前 PHP 对变量的内存控制还有 Bug 的时候,可能一个脚本已经运行完,但使用的数据内存却没有被释放,这时使用这个选项能起到很大的作用。现在 PHP7 内存控制得还不错,但如果有遇到内存随着 PHP 进程运行时间变长而慢慢变大的情况,除了后期检查代码之外,第一时间可以先开启这个选项强行重启 PHP 进程来解决。

还有一个是 pm.process_idle_timeout。对于可能会销毁进程的模式,这个值定义了如果一个 PHP 进程空闲了多长时间就需要被销毁。

跟资源相关的选项就说完了,这里补充一句,www.conf 也是可以覆盖 php.ini 选项的,比如 memory_limit 选项就可以通过下面方式在 www.conf 里覆盖:

php_admin_value[memory_limit] = 32M

复写配置的方式其实有两种,定义 php_value[xxx] 或者 php_admin_value[xxx],它们唯一的区别是,php_value 可以在代码运行时再被 ini_set 函数覆盖而 php_admin_value 不行。

www.conf 文件还有别的有意思的选项,比如可以开启一个路径返回 FPM 的运行状态啊,以及配置日志路径等等,特别是慢日志记录(slowlog),对生产环境非常有用,这些都是能让我们观察到 FPM 运行状态的好方法。对运维工作来说,监控所有工具的运行状态,是一个很重要,也很有挑战性的工作。

配置日志应该是很简单直白的事情,这里就不多说了。FPM 状态查询页需要在 www.conf 以及 NGINX 里都要配置才能管用,下面是简单的配置实例供大家参考:

# 在 www.conf 文件的配置
pm.status_path = /status

# NGINX 里需要针对 /status 路径做配置
server {
  # 站点相关配置这里省略,可以参考上一篇文章的配置

  location = /status {
    include fastcgi.conf;
    fastcgi_pass 127.0.0.1:9000; # 需要跟 www.conf 的 listen 选项配置一致
    # 你当然不希望外网随便一个人都能查看 FPM 的运行状态,所以要对访问 IP 做限制
    allow 127.0.0.1;
    deny all;
  }
}

人人都能看懂的全栈开发教程——PHP-FPM by Chris Yue is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License.

微信赞赏码

如果觉得文章还不错,就请扫码鼓励一下作者吧
天使打赏人

发表评论

+ 87 = 95