vlambda博客
学习文章列表

Nginx 与 PHP-FPM 的工作机制(强烈建议阅读,面试中常问,却很少有人能说清楚)

在介绍Nginx和PHP-FPM的工作机制之前,先了解下面几个名词:

名词解释

CGI:CGI的英文是(COMMON GATEWAY INTERFACE)公共网关接口,是 Web Server 与 Web Application 之间数据交换的一种协议。它的作用就是帮助服务器与后端语言通信,这里就是nginx和php进行通信,因为nginx和php的语言不通,因此需要一个沟通转换的过程,而CGI就是这个沟通的协议。

FastCGI:同 CGI,是一种通信协议,但比 CGI 在效率上做了一些优化。

PHP-CGI:是 PHP (Web Application)对 Web Server 提供的 CGI 协议的接口程序。

PHP-FPM:它是 FastCGI 的实现,是 PHP(Web Application)对 Web Server 提供的 FastCGI 协议的接口程序。PHP-FPM 是一个 PHP 进程管理器,包含 master 进程和 worker 两种进程:master 进程只有一个,负责监听端口,接收来自 Web Server 的请求,而 worker 进程则一般有多个 (具体数量根据实际需要配置),每个进程内部都嵌入了一个 PHP 解释器,是 PHP 代码真正执行的地方。



了解完这些名词之后,我们开始来介绍一下Nginx、PHP-FPM的工作方式,以及二者之间是如何通信的。

如上面所介绍,CGI 是 Web Server 与后台语言交互的协议,有了这个协议,开发者可以使用任何语言处理 Web Server 发来的请求,动态的生成内容。但 CGI 有一个致命的缺点,那就是每处理一个请求都需要 fork 一个CGI进程,请求结束再 kill 掉这个进程,在实际应用上比较浪费资源。随着 Web 的兴起,高并发越来越成为常态,这样低效的方式显然不能满足需求。于是就出现了 CGI 的改良版本 FastCGI,逐渐的CGI 就退出了历史的舞台。FastCGI顾名思义是更快的 CGI,FastCGI 在请求处理完后,不会 kill 掉进程,而是继续处理多个请求,这样就大大提高了效率。

至于 FPM (FastCGI Process Manager),它是 FastCGI 的实现,任何实现了 FastCGI 协议的 Web Server 都能够与之通信。

FPM 是 PHP 进程管理器,包含 master 进程和 worker 进程:master 进程只有一个,负责监听端口,接收来自 Web Server 的请求,而 worker 进程则一般有多个 (具体数量根据实际需要配置),每个进程内部都嵌入了一个 PHP 解释器,是 PHP 代码真正执行的地方。

PHP-FPM处理请求的流程
  1. FPM 的 master 进程接收到请求;

  2. master 进程根据配置指派特定的 worker 进程进行请求处理,如果没有可用进程,返回502错误;

  3. worker 进程处理请求,如果超时,返回504错误;

  4. 请求处理结束,返回结果。


Nginx 如何发送请求给 fpm 的呢?

我们知道,Nginx 不仅仅是一个 Web 服务器,也是一个功能强大的 Proxy 服务器,除了进行 http 请求的代理,也可以进行许多其他协议请求的代理,包括本文与 fpm 相关的 fastcgi 协议。为了能够使 Nginx 理解 fastcgi 协议,Nginx 提供了 fastcgi 模块来将 http 请求映射为对应的 fastcgi 请求。

Nginx 的 fastcgi 模块提供了 fastcgi_param 指令来主要处理这些映射关系,下面是 Nginx 的一个配置文件,其主要完成的工作是将 Nginx 中的变量翻译成 PHP 中能够理解的变量。

Nginx 与 PHP-FPM 的工作机制(强烈建议阅读,面试中常问,却很少有人能说清楚)


在这个配置文件中,我们新建了一个虚拟主机,监听在 80 端口,Web 根目录为 /data/web/bi/public/。然后我们通过 location 指令,将所有的以 .php 结尾的请求都交给 fastcgi 模块处理,从而把所有的 php 请求都交给了 fpm 处理,从而完成 Nginx 到 fpm 的闭环。 

下面附上nginx与php-fpm通信的流程图,如下:


其实php与nginx通信有两种方式,上面我们介绍的是tcp socket的方式,下面我们再介绍一下另外一种通信方式 unix socket(感兴趣的可以了解):


tcp socket 的优点是可以跨服务器,当 nginx 和 php-fpm 不在同一台机器上时,只能使用这种方式。


unix socket 又叫 IPC (inter-process communication 进程间通信) socket,用于实现同一主机上的进程间通信,这种方式需要在 nginx 配置文件中填写 php-fpm 的 socket 文件位置。


由于 Unix socket 不需要经过网络协议栈,不需要打包拆包、计算校验和、维护序号和应答等,只是将应用层数据从一个进程拷贝到另一个进程。所以其效率比 tcp socket 的方式要高,可减少不必要的 tcp 开销。不过,unix socket 高并发时不稳定,连接数爆发时,会产生大量的长时缓存,在没有面向连接协议的支撑下,大数据包可能会直接出错不返回异常。而 tcp 这样的面向连接的协议,可以更好的保证通信的正确性和完整性。


nginx、php配置文件


tcp socket 方式

# nginx配置server { listen 80; server_name demo.top-linux.com;
root /data/web/bi/public/; index index.php index.html index.htm; try_files $uri $uri/ @rewrite;
add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Headers X-Requested-With; add_header Access-Control-Allow-Methods GET,POST,OPTIONS;
location @rewrite { rewrite ^/(.*)$ /index.php?_url=/$1; }
location ~ \.php { fastcgi_pass 127.0.0.1:9000; # tcp socket方式,php-fpm 监听的 IP 地址和端口 fastcgi_index /index.php; fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_param PATH_INFO $fastcgi_path_info; fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params;    }}
# php-fpm配置listen = 127.0.0.1:9000


unix socket方式

# nginx配置server { listen 80; server_name demo.top-linux.com;
root /data/web/bi/public/; index index.php index.html index.htm; try_files $uri $uri/ @rewrite;
add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Headers X-Requested-With; add_header Access-Control-Allow-Methods GET,POST,OPTIONS;
location @rewrite { rewrite ^/(.*)$ /index.php?_url=/$1; }
location ~ \.php {        fastcgi_pass                  /usr/run/php-fpm.sock; # unix socket 连接方式 fastcgi_index /index.php; fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_param PATH_INFO $fastcgi_path_info; fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params;    }}
# php-fpm配置listen = /var/run/php-fpm.sock


说完了,收工!如有错误,欢迎指正