Nginx与PHP-FPM


CGI、FastCGI、PHP-FPM的区别

CGI(Common Gateway Interface) 代表“通用网关接口”。 它是一个标准协议,定义 Web 服务器如何与外部应用程序或脚本交互以处理 HTTP 请求并生成动态 Web 内容。 在 PHP、Python 和 Ruby 等服务器端脚本语言出现之前,CGI 是最早用于动态网页生成的方法之一。 当客户端(通常是 Web 浏览器)向 Web 服务器发送 HTTP 请求时,服务器会处理该请求,并且在静态内容(如 HTML 和图像)的情况下,直接提供所请求的文件。 然而,当请求的内容是动态的并且需要即时生成时,服务器可以使用CGI与外部程序通信来进行处理。 以下是 CGI 工作原理的基本概述:

1.客户端向 Web 服务器发送 HTTP 请求。

2.Web服务器接收请求并识别出请求的内容需要动态处理。

3.Web 服务器将请求连同请求中的必要环境变量和数据(例如,查询参数、表单数据等)一起传递给 CGI 程序或脚本。

4.CGI 程序处理请求并生成动态内容,通常采用 HTML 形式。

5.CGI程序将生成的内容发送回Web服务器。

6.Web 服务器将 HTTP 响应中的动态内容返回给客户端,客户端将其呈现在 Web 浏览器中。

CGI 程序可以用各种编程语言编写,例如 Perl、Python、Ruby、C、C++ 等。它们作为与 Web 服务器分开的进程执行,这意味着对 CGI 程序的每个请求都会产生启动新进程的开销。 虽然 CGI 在当时具有革命性意义并支持动态 Web 内容,但它也有一定的局限性,包括性能开销和可扩展性问题。 因此,现代 Web 开发已在很大程度上摆脱了 CGI,转而采用更高效、可扩展的服务器端脚本技术,例如 PHP、Node.js 和各种应用程序框架。 如今,CGI 主要用于需要与遗留系统或专用环境兼容的特定情况。 对于通用 Web 开发,首选更高效的替代方案。

FastCGI

FastCGI 是通用网关接口 (CGI) 的变体,它解决了传统 CGI 的性能和可扩展性限制。 它是一种协议,允许 Web 服务器与外部应用程序进程高效通信,从而为 Web 应用程序生成动态内容。 使用传统的 CGI,每个请求都会生成一个新进程,从而导致显着的性能开销。 FastCGI 通过引入持久应用程序进程池对此进行了改进,即使在处理请求后该进程池仍保持活动状态。 该进程池无需为每个传入请求启动新进程,从而减少了开销并提供更好的性能和资源利用率。 FastCGI 的工作原理如下:

1.客户端向 Web 服务器发送 HTTP 请求。

2.Web 服务器通过套接字或 TCP/IP 连接将请求以及必要的环境变量和请求数据传递给 FastCGI 应用程序。

3.FastCGI 应用程序处理请求并生成动态内容(例如,HTML、JSON 等)。

4.FastCGI 进程并没有终止应用程序进程,而是保持活动状态并等待下一个请求。

5.Web 服务器接收来自 FastCGI 应用程序的响应,并在 HTTP 响应中将其发送回客户端。

FastCGI 与传统 CGI 相比具有以下几个优点:

1.减少开销:通过重用应用程序进程,FastCGI 消除了为每个请求创建和终止进程的开销。

2.改进的性能:持久进程池可以加快后续请求的响应时间,因为应用程序不需要为每个新请求重新加载。

3.资源效率:与为每个请求生成新进程相比,持久进程池消耗的资源更少。

4.可扩展性:FastCGI允许Web服务器处理更高数量的并发请求,提高Web应用程序的可扩展性。

FastCGI 广泛用于 Web 服务器配置中,以有效处理动态内容生成。 Nginx 和 Apache 等流行的 Web 服务器支持 FastCGI,使其成为托管用 PHP、Python、Ruby 等语言编写的动态 Web 应用程序的标准选择。 总而言之,FastCGI 是 CGI 的扩展,通过采用持久的应用程序进程池来处理传入请求,显着提高了 Web 应用程序的性能和资源利用率。

PHP-FPM

图片

PHP-FPM(FastCGI Process Manager)是 PHP-FPM 的另一种 PHP FastCGI 实现,广泛用于高效、安全地为 PHP 应用程序提供服务。 它与 Web 服务器(例如 Nginx 或 Apache)结合使用,通过 FastCGI 协议处理 PHP 请求。 与传统 PHP CGI 或 mod_php(Apache PHP 模块)相比,PHP-FPM 提供了多项优势:

1.FastCGI进程管理:PHP-FPM使用进程管理器来控制PHP子进程。 这些子进程处理传入的 PHP 请求,PHP-FPM 根据配置设置管理它们。 进程管理器可以更好地控制进程生成、终止和资源管理,从而提高性能并减少资源使用。

2.资源池:PHP-FPM 维护一个工作进程池,这些进程保持活动状态并准备好处理传入请求。 这避免了为每个请求启动和停止 PHP 进程的开销,使 PHP-FPM 比传统 CGI 更高效、更快。

3.隔离和安全性:PHP-FPM 在单独的隔离进程中运行 PHP 请求,通过将各个 PHP 请求相互隔离来增强安全性。 这种隔离可以防止不同 PHP 请求之间潜在的数据共享,并提高 PHP 应用程序的整体安全性。

4.可定制的配置:PHP-FPM允许用户定制流程管理器的行为和配置,以匹配服务器的资源和要求。 这使用户能够微调 PHP-FPM 以获得最佳性能和稳定性。

5.扩展:PHP-FPM 的进程管理通过调整 PHP 工作进程的数量来处理增加的流量和工作负载,可以更轻松地水平扩展 PHP 应用程序。

PHP-FPM 是在现代 Web 服务器配置中为 PHP 应用程序提供服务的推荐方法。 它通常与 Nginx 一起使用,但也可以与 Apache 或其他支持 FastCGI 的 Web 服务器一起使用。 要使用 PHP-FPM,您通常需要将其与 Web 服务器分开安装,并将其配置为与您选择的 Web 服务器一起使用。 配置过程根据您使用的操作系统和 Web 服务器而有所不同,但大多数 PHP 安装都自带了 PHP-FPM 。 总体而言,PHP-FPM 是 PHP 应用程序部署的宝贵工具,可为 PHP 驱动的 Web 应用程序提供改进的性能、安全性和可扩展性。

总而言之,CGI 是执行服务器端脚本最简单、最古老的方法,但性能开销较大。 FastCGI 通过引入持久进程池对 CGI 进行了改进,从而获得了更好的性能。 另一方面,PHP-FPM 是为 PHP 执行量身定制的特定 FastCGI 实现,为 PHP 应用程序提供卓越的性能、可扩展性和安全性。 对于现代 PHP 应用程序,PHP-FPM 是处理动态内容的推荐方法。

PHP-Cli

PHP 的命令行界面(Command Line Interface)首次出现在 PHP 4.2.0 中,作为实验性功能。在 PHP 4.3.0 中,CLI 成为正式的 SAPI(服务器 API)。实际上,这意味着 PHP 的 CLI 版本现在作为单独的 PHP 二进制文件(可执行文件)提供,您可以使用它在命令行上运行脚本,并为世界其他地方提供“访问点”,就像 $_GET 和 $_POST 变量让您可以通过 Web 访问传入数据。自PHP5.4之后 PHP开始内置了一个Web 服务器。通过 php -S 命令即可启动PHP自带的Web Server,后面跟网络地址及监听的端口号,默认的网站根目录为当前目录。访问 http://localhost:8080,通过php-cli,php直接接管了nginx或者apache的http服务工作,但cli内置的服务器功能比较弱的。一般都是建议开发环境使用而已。,而在swoole框架中,php-cli称成为了其唯一运行环境。并且swoole增强了其作为服务器的功能。有点想springboot集成了tomcat 的意思。使用swoole可以直接开发各种服务器。 cli环境一般有以下优点

  • 不依赖于Web服务器,可以在不使用Web服务器的情况下运行PHP脚本。
  • 可以轻松地在命令行中运行PHP脚本,测试脚本时非常方便,例如在执行一些系统任务、批处理任务、计划任务、测试脚本等方面。

Nginx 与 PHP-FPM 交互过程

以常见的 LNMP 架构为例,Nginx 在一台服务器上,PHP 在另一台服务器上。

当我们请求一个域名的时候,比如一个测试域名,www.oiox.cn 域名解析到 Nginx 服务器,Nginx 路由到 index.php 文件。此时 Nginx 检测出这不是静态文件,需要找 PHP 解析器来解析,然后加载 Nginx 的 FAST_CGI 模块,并 fastcgi_pass 到 PHP 服务器,比如 10.20.0.1:9000。此时 PHP 服务器上的 PHP-FPM 子进程监听到了请求,然后处理请求。其流程如下:

浏览器访问 www.oiox.cn
|
域名解析到 Nginx 服务器
|
路由到 www.oiox.cn/index.php
|
Nginx 检测 index.php 不是静态资源,加载 Nginx 的 fast-cgi 模块
|
请求被转发到 PHP 所在的服务器上
|
PHP 服务器上的 fast-cgi 监听 127.0.0.1:9000 地址
|
www.oiox.cn/index.php 请求到达 127.0.0.1:9000
|
PHP-FPM worker 进程执行代码

Nginx 与 PHP-FPM 通信方式

在 Linux 上,Nginx 和 PHP-FPM 通信有两种方式,tcp-socket 和 unix-socket。 当 Nginx 和 PHP-FPM 不在同一台机器上时,只能使用 tcp-socket 这种通信方式。

tcp socket 和 unix socket 对比

  • 效率:理论上,Unix domain socket 不需要经过网络协议栈,不需要打包拆包、计算校验和、维护序列号和应答等,只是将应用层数据从一个进程拷贝到另一个进程。所以其效率比 tcp socket 的方式要高,可减少不必要的 tcp 开销。
  • 跨机器通信:tcp socket 支持跨机器通信,而 unix domain socket 是同一机器进程之间通信。
  • 高并发:实际上,在高并发情况下,两者的性能差距并不明显。但是 tcp socket 能表现出很明显的更高的稳定性

安装PHP

# 安装php8.1
apt install php8.1-fpm

# 设置开机自启
root@cby:~# systemctl enable  php8.1-fpm
Synchronizing state of php8.1-fpm.service with SysV service script with /lib/systemd/systemd-sysv-install.
Executing: /lib/systemd/systemd-sysv-install enable php8.1-fpm
root@cby:~# 

# 查看服务是否正常
root@cby:~# systemctl status php8.1-fpm.service 
● php8.1-fpm.service - The PHP 8.1 FastCGI Process Manager
     Loaded: loaded (/lib/systemd/system/php8.1-fpm.service; enabled; vendor preset: enabled)
     Active: active (running) since Wed 2023-12-06 15:57:31 CST; 1min 19s ago
       Docs: man:php-fpm8.1(8)
    Process: 22149 ExecStartPost=/usr/lib/php/php-fpm-socket-helper install /run/php/php-fpm.sock /etc/php/8.1/fpm/pool.d/www.conf 81 (code=exited, s>
   Main PID: 22146 (php-fpm8.1)
     Status: "Processes active: 0, idle: 2, Requests: 0, slow: 0, Traffic: 0req/sec"
      Tasks: 3 (limit: 3943)
     Memory: 7.1M
        CPU: 25ms
     CGroup: /system.slice/php8.1-fpm.service
             ├─22146 "php-fpm: master process (/etc/php/8.1/fpm/php-fpm.conf)" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ""
             ├─22147 "php-fpm: pool www" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" >
             └─22148 "php-fpm: pool www" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" >

Dec 06 15:57:31 cby systemd[1]: Starting The PHP 8.1 FastCGI Process Manager...
Dec 06 15:57:31 cby systemd[1]: Started The PHP 8.1 FastCGI Process Manager.
root@cby:~# 

安装NGINX

# 安装NGINX
apt install nginx

# 修改NGINX配置
root@cby:~# cat /etc/nginx/sites-available/default
server {
    listen 80 default_server;
    listen [::]:80 default_server;
    root /var/www/html;
    index index.php index.html index.htm index.nginx-debian.html;
    server_name _;
    location / {
        try_files $uri $uri/ =404;
    }
    location ~ \.php$ {
           include snippets/fastcgi-php.conf;
           fastcgi_pass unix:/run/php/php8.1-fpm.sock;
    }
}
root@cby:~# 

测试页面

# 写入测试页面
root@cby:~# echo "<?php phpinfo(); ?>" >> /var/www/html/php.php

# 重启NGINX
root@cby:~# systemctl restart nginx
# 设置开机自启NGINX
root@cby:~# systemctl enable nginx
Synchronizing state of nginx.service with SysV service script with /lib/systemd/systemd-sysv-install.
Executing: /lib/systemd/systemd-sysv-install enable nginx
# 查看状态
root@cby:~# systemctl status nginx
● nginx.service - A high performance web server and a reverse proxy server
     Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled)
     Active: active (running) since Wed 2023-12-06 16:25:33 CST; 36s ago
       Docs: man:nginx(8)
    Process: 30554 ExecStartPre=/usr/sbin/nginx -t -q -g daemon on; master_process on; (code=exited, status=0/SUCCESS)
    Process: 30555 ExecStart=/usr/sbin/nginx -g daemon on; master_process on; (code=exited, status=0/SUCCESS)
   Main PID: 30556 (nginx)
      Tasks: 5 (limit: 3943)
     Memory: 5.2M
        CPU: 24ms
     CGroup: /system.slice/nginx.service
             ├─30556 "nginx: master process /usr/sbin/nginx -g daemon on; master_process on;"
             ├─30557 "nginx: worker process" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ""
             ├─30559 "nginx: worker process" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ""
             ├─30560 "nginx: worker process" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ""
             └─30561 "nginx: worker process" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ""

Dec 06 16:25:33 cby systemd[1]: Starting A high performance web server and a reverse proxy server...
Dec 06 16:25:33 cby systemd[1]: Started A high performance web server and a reverse proxy server.
root@cby:~# 


# 访问测试页面
http://[主机IP]/php.php

PHP-FPM+Nginx通信

FastCGI致力于减少Web服务器与CGI程序之间互动的开销,从而使服务器可以同时处理更多的Web请求。与CGI这种为每个请求创建一个新的进程不同,FastCGI使用持续的进程来处理一连串的请求。

这些进程由FastCGI进程管理器管理,而不是web服务器。通过图来理解PHP-FPM和Nginx的通信

图片

  • (1)当Nginx收到http请求(动态请求),它会初始化FastCGI环境。(如果是Apache服务器,则初始化mode_fastcgi模块、如果是Nginx服务器则初始化ngx_http_fastcgi_module)
  • (2)我们在配置nginx解析php请求时,一般会有这样一行配置:
fastcgi_pass   127.0.0.1:9000;

或者

fastcgi_pass  unix:/tmp/php-cgi.sock;

它其实是Nginx和PHP-FPM一个通信载体(或者说通信方式),目的是为了让Nginx知道,收到动态请求之后该往哪儿发。(关于这两种配置的区别,后边会专门介绍)

  • (3)Nginx将请求采用socket的方式转给FastCGI主进程
  • (4)FastCGI主进程选择一个空闲的worker进程连接,然后Nginx将CGI环境变量和标准输入发送该worker进程(php-cgi)
  • (5)worker进程完成处理后将标准输出和错误信息从同一socket连接返回给Nginx
  • (6)worker进程关闭连接,等待下一个连接

PHP和Nginx的通信

  • 我们知道Nginx也是有master和worker进程的,worker进程直接处理每一个网络请求
  • 其实在Nginx+PHP的架构里边,php可以看做是一个cgi程序的角色,因此出现了php-fpm进程管理器来处理这些php请求。php-fpm和nginx一样,也会监听端口(通过nginx.conf里的配置我们知道,nginx默认监听8080端口,php-fpm默认监听9000端口),并且有master和worker进程,worker负责处理每一个php请求
  • 关于fastcgifastcgi是一个协议。市面上有多种实现了fastcgi协议的进程管理器,php-fpm就是其中的一种。php-fpm作为一种fastcgi进程管理服务,会监听端口,一般默认监听9000端口,并且是监听本机,也就是只接收来自本机的端口请求
  • 关于fastcgi的配置文件,目前fastcgi的配置文件一般放在nginx.conf同级目录下,配置文件形式,一般有两种:fastcgi.conffastcgi_params。

不同的nginx版本会有不同的配置文件,这两个配置文件有一个非常重要的区别:fastcgi_parames文件中缺少下列配置:

fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;

我们可以打开fastcgi_params文件加上上述行,也可以在要使用配置的地方动态添加。使得该配置生效

1. 第一种:文件地址

/usr/local/openresty/nginx/conf/fastcgi.conf

2. 第二种:文件地址为nginx server的php配置项。使用配置的地方动态添加:

fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
  • 当需要处理php请求时,nginx的worker进程会将请求移交给php-fpm的worker进程进行处理,也就是最开头所说的nginx调用了php,其实严格得讲是nginx间接调用php(反向代理的方式)
    location ~ \.php$ {
        try_files $fastcgi_script_name =404;
        include fastcgi_params;
        # fastcgi_pass unix:/var/run/php-fpm.sock;
        fastcgi_pass 127.0.0.1:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }
  • include fastcgi_params; 引入fastcgi配置文件
  • fastcgi_pass 127.0.0.1:9000; 这行代码的意思是,将进入到该location内的uri请求看做是cgi程序,并将请求发送到9000端口,交由php-fpm处理(php-fpm配置中会看见它监听了此端口)
  • fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;。这行配置意思是:动态添加了一行fastcgi配置,配置内容为SCRIPT_FILENAME,告知管理进程,cgi脚本名称。由于我的nginx中只有fastcgi_params文件,没有fastcgi.conf文件,所以要使php-fpm知道SCRIPT_FILENAME的具体值,就必须要动态的添加这行配置

fastcgi_pass

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

Tcp Socket方式是IP+端口,可以跨服务器,而UNIX Socket不经过网络,只能用于Nginx跟PHP-FPM都在同一服务器的场景,用哪种取决于你的PHP-FPM配置。

Tcp Socket方式

  • nginx.conf中配置:fastcgi_pass 127.0.0.1:9000;
  • php-fpm.conf中配置:listen=127.0.0.1:9000;
  • Nginx和PHP-FPM在同一台机器上,通信过程:Nginx <=> socket <=> TCP/IP <=> socket <=> PHP-FPM
  • Nginx和PHP-FPM不在同一台机器上通信过程:Nginx <=> socket <=> TCP/IP <=> 物理层 <=> 路由器 <=> 物理层 <=> TCP/IP <=> socket <=> PHP-FPM

Unix Domain Socket方式

  • nginx.conf中配置:fastcgi_pass unix:/tmp/php-fpm.sock;
  • php-fpm中配置:listen = /tmp/php-fpm.sock;(php-fpm.sock是一个文件,由php-fpm生成)
  • Nginx和PHP-FPM的通信过:Nginx <=> socket <=> PHP-FPM