nginx 和 PHP-fpm 的交互

nginx 是一个 webservice 的功能,当检测到访问 php 文件时,会把请求交给 fastcgi 模块处理。我们可以在 nginx 的配置文件中配置 fastcgi 的相关参数

nginx和php-fpm的交互
nginx 的 fastcgi 模块会把请求交给 php-fpm 程序处理。php-fpm 的 worker 进程处理完后,会把数据返回给 nginx,nginx 会把数据放到内存缓存中,缓存区大小为 fastcgi_buffer_size,fastcgi_buffers 控制。如果返回数据大于缓存,则多出来的数据会被临时写入到文件中,放在 fastcgi_temp 目录下面。

PHP-FPM#

php-fpm 是一种 master(主)/worker(子)多进程架构
master 的工作分为四步:
1.cgi 初始化阶段:分别调用 fcgi_init () 和 sapi_startup () 函数,注册进程信号以及初始化 sapi_globals 全局变量。

2.php 环境初始化阶段:a). 加载和解析 php 配置;b). 加载 php 模块并记入函数符号表 (function_table);c). 加载 zend 扩展;d). 设置禁用函数和类库配置;e). 注册回收内存方法;

3.php-fpm 初始化阶段:执行 fpm_init () 函数。负责解析 php-fpm.conf 文件配置,获取进程相关参数(允许进程打开的最大文件数等), 初始化进程池及事件模型等操作。

4.php-fpm 运行阶段:执行 fpm_run () 函数,运行后主进程发生阻塞。该阶段分为两部分:fork 子进程 和 循环事件。fork 子进程部分交由 fpm_children_create_initial 函数处理( 注:ondemand 模式在 fpm_pctl_on_socket_accept 函数创建)。循环事件部分通过 fpm_event_loop 函数处理,其内部是一个死循环,负责事件的收集工作。

worker 进程分三步:
1. 接收客户端请求:执行 fcgi_accept_request 函数,其内部通过调用 accept 函数获取客户端请求。

2. 处理请求阶段:首先,分别调用 fpm_request_info、php_request_startup 获取请求内容及注册全局变量 ($_GET、$_POST、$_SERVER、$_ENV、$_FILES);然后根据请求信息调用 php_fopen_primary_script 访问脚本文件;最后交给 php_execute_script 执行。php_execute_script 内部调用 zend_execute_scripts 方法将脚本交给 zend 引擎处理。

3. 请求结束阶段:执行 php_request_shutdown 函数。此时 回调 register_shutdown_function 注册的函数及__destruct () 方法,发送响应内容、释放内存等操作。

PHP-FPM 的三种模式#

php-fpm.conf 配置 pm
pm=static

nginx 和 PHP-fpm 的交互

始终保持固定数量的 worker 进程数,由 pm.max_children 决定。
pm=dynamic
php-fpm 启动时,会初始启动一些 worker,初始启动 worker 数决定于 pm.max_children 的值。在运行过程中动态调整 worker 数量,worker 的数量受限于 pm.max_children 配置,同时受限全局配置 process.max。
pm=ondemand
php-fpm 启动的时候,不会启动任何一个 worker,而是按需启动,只有当连接过来的时候才会启动。
启动的最大 worker 数决定于 pm.max_children 的值,同时受限全局配置 process.max。
1 秒定时器作用,如果空闲 worker 时间超过 pm.process_idle_timeout 的值(默认值为 10s),则关闭该 worker。这个机制可能会关闭所有的 worker。

worker 数量不足
当 master 监听到连接事件时,发现 worker 进程数量不够,会把连接放入到一个等待队列中,一直等待到有新的 worker 进程创建出来,就从等待等待队列中获取一个连接,然后 accept 请求处理。当连接太多而 worker 数量太少时,等待的时间太长,超过了 nginxf 的 fastcgi 连接时间限制,就会返回 504 超时。

常见错误码#

php-fpm 的 work 进程超时情况:
php-fpm:
request_terminate_timeout = 0
php.ini:
max_execution_time = 30 // 这个参数记录的是 php 执行时间比较,不包括数据交互,网络连接的时间,所以如果不设置 request_terminate_timeout 的话,会导致 worker 永不超时,占用 php-fpm 的 worker. 直到超过 fastcgi_connect_timeout 的设置,返回 504.

request_terminate_timeout 适用于,当 max_execution_time 由于某种原因无法终止脚本的时候(连接数据库,sleep 等),会把这个 php-fpm 请求干掉。所以最好是在 php-fpm.conf 加上这个设置。

502:PHP-FPM 没有开启或者 php-fpm 运行时手动 kill 掉对应的 worker 进程,php-fpm 没有正常返回数据给 nginx, 报 502 bad gateway.

500:当 max_execution_time 或者 request_terminate_timeout 生效的时候,php-fpm 主动 kill 这个 worker 进程,php 有 Fatal error 超时日志,http 状态码为 500。php 无 Fatal error 超时日志,http 状态码为 502,php-fpm 日志中有杀掉子进程日志

504:nginx 的 Fastcgi 的参数 fastcgi_connect_timeout 超时,就是说 php-fpm 的执行时间超过了 nginx 的 fastcgi 连接时间限制。

nginx 与 php-fpm 的通讯方式#

Nginx 和 PHP-FPM 的进程间通信有两种方式,一种是 TCP, 一种是 UNIX Domain Socket.

tcp#

允许通过网络进程之间的通信,也可以通过 loopback 进行本地进程之间通信。

UNIX Domain Socket#

允许在本地运行的进程之间进行通信。
其中 TCP 是 IP 加端口,可以跨服务器。而 UNIX Domain Socket 不经过网络,只能用于 Nginx 跟 PHP-FPM 都在同一服务器的场景。用哪种取决于你的 PHP-FPM 配置:

方式1:
php-fpm.conf: listen = 127.0.0.1:9000
nginx.conf: fastcgi_pass 127.0.0.1:9000;
方式2:
php-fpm.conf: listen = /tmp/php-fpm.sock
nginx.conf: fastcgi_pass unix:/tmp/php-fpm.sock;
本作品采用《CC 协议》,转载必须注明作者和本文链接
走出舒适区
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。