PHP底层原理:CGI、FastCGI与PHP-FPM

----- 最近更新【2022-01-28】-----

本文目录结构预览:

  • 一、简介
  • 二、相关概念
    1、Web 服务器
    2、SAPI
    3、Zend
  • 三、工作原理
    1、静态请求
    2、CGI 模式
    3、FastCGI 模式
  • 四、CGI 与 FastCGI 的区别
  • 五、参考

一、简介

在搭建 LAMP/LNMP 服务器时,会经常遇到 CGI、FastCGI 或 PHP-FPM 等等几个相关个概念。如果对它们一知半解,很难搭建出高性能的服务器。本文会以图文的形式,解释这些概念之间的关系。

在这之前,先了解一下相关的概念。

二、相关概念

1、Web 服务器

Web 服务器是用于处理 html 等静态资源,让客户可以通过浏览器进行访问。主流的 Web 服务器有 Apache、IIS、Nginx、Lighttpd 等。

2、SAPI

SAPI(Server Application Programming Interface)服务器应用程序编程接口,即一个 PHP 与其他应用交互的接口。PHP 脚本要执行有很多种方式,可以通过 Web 服务器,或者直接在命令行下执行,也可以嵌入在其他程序中。而 SAPI 在这里扮演的角色,则是提供了一个与外部应用(如 Nginx 服务器)通信的接口,使得 PHP 可以和其他应用进行交互数据。

PHP 本身可以理解为是一个库函数,提供语言的编译与执行服务,它有标准的输入、输出。而 SAPI 则是 PHP 的接入层,它接收用户的请求,然后调用 PHP 内核提供的一些接口完成 PHP 脚本的执行,所以严格意义上讲 SAPI 并不算 PHP 内核的一部分。

PHP 中常用的 SAPI 有 CGI、FastCGI、apache2handler、CLI 等。CLI 是命令行下执行 PHP 脚本的实现,它是单进程的,处理模型比较简单。而 CGI、FastCGI 相对比较复杂,它实现了网络处理模块,用于与 web 服务器交互。

通过查看 PHP 源码的/sapi目录也可以方便的查看到当前 PHP 版本都支持哪些 SAPI。如:

[d:\Downloads\php-8.1.2\sapi]$ list
Directory of d:\Downloads\php-8.1.2\sapi

Name                      Host                  Protocol  User Name  
------------------------- --------------------- --------- -----------
apache2handler/
cgi/
cli/
embed/
fpm/
fuzzer/
litespeed/
phpdbg/

3、Zend

Zend 是 PHP 语言实现的最为重要的部分,是PHP最基础、最核心的部分,它的源码在/Zend目录下。PHP 代码从编译到执行都是由 Zend 完成的。Zend 整体由两个部分组成:

  • 编译器: 负责将 PHP 代码编译为抽象语法树,然后进一步编译为可执行的 opcodes,这个过程相当于 gcc 的工作,编译器是一个语言实现的基础。
  • 执行器: 负责执行编译器输出的 opcodes,也就是执行 PHP 脚本中编写的代码逻辑。

三、工作原理

1、静态请求

早期的 Web 服务只是按照客户端请求将保存在 Web 服务器硬盘中的数据转发过去而已,这种情况下客户端每次获取的信息也是同样的内容(即静态请求,比如图片、css 文件、HTML 文件等)。

2、CGI 模式

通常来自客户端的请求被 Web 服务器截获,如果是静态请求,则 Web 服务器(如:nginx)会自己做处理。如果是动态请求,则会抛给后端应用服务器(如:php-cgi)来处理。所以,如何在 web 服务器与应用服务器之间进行通信成了主要的问题,这也就是 SAPI 的作用。CGI 是就是其中一种 SAPI 规范,而 php-cgi 就是 CGI 的具体实现。

CGI(Common Gateway Interface)是一种通用网关接口规范,该规范详细描述了 Web 服务器(如:nginx)和后端应用服务器(如:php-cgi)在获取及返回数据过程中传输数据的标准。(类似于 Web 浏览器和 Web 服务器进行数据交换的协议是 HTTP 协议)

也就是说,CGI 就是专门用来和 Web 服务器打交道的。Web 服务器收到用户请求,就会把请求提交给 CGI 程序(如 php-cgi),CGI 程序根据请求提交的参数作出对应处理(解析php),然后输出标准的 HTML 资源,返回给 Web 服务器,Web 服务器再返回给客户端,这就是 CGI 的工作原理。

CGI 程序可以用任何一种语言编写,只要这种语言具有标准输入、输出和环境变量。CGI 的好处就是完全独立于任何服务器,仅仅是做为中间分子。例如,提供接口给 Nginx 和 php。他们通过 CGI 搭线来完成数据传递。这样做的好处就是尽量减少了这两个程序的关联,使他们变得更独立。

3、FastCGI 模式

FastCGI(Fast Common Gateway Interface)快速通用网关接口,是 CGI 的增强版本,为了提升 CGI 的性能而生。

PHP-FPM(FastCGI Process Manager for PHP)PHP 的 FastCGI 进程管理器。FastCGI 只是一个协议规范,需要某个程序去具体实现,而 PHP-FPM 就是这个具体实现。

在 CGI 模式中,CGI 程序(即 php-cgi)针对每个 HTTP 请求都会 fork 一个新的php-cgi 进程来进行处理(解析配置文件、初始化执行环境、处理请求等),然后把这个进程处理完的结果通过 Web 服务器转发给客户端,这个php-cgi 进程也随之退出。如果下次用户再请求动态资源,那么 CGI 程序又再次 fork 一个新的php-cgi 进程,如此周而复始循环往复。我们也把这叫 fork-and-execute 模式,如果在大规模并发下,这种模式就会显得非常无力。

而 FastCGI 模式中, FastCGI 程序(即 PHP-FPM)会先 fork 一个master 进程,解析配置文件,初始化执行环境,然后再 fork 多个worker 进程master 进程负责与 Web 服务器进行通信,当收到 HTTP 请求时,master 进程将其会传递给其中一个空闲的worker 进程,然后立即可以继续接受下一个请求。这样就避免了重复的初始化操作,效率自然也就提高了。而且当worker 进程不够用时,master 进程还可以根据配置预先启动几个worker 进程等着;当空闲worker 进程太多时,也会关掉一些,这样不仅提高了性能,还节约了系统资源。worker 进程主要负责调用 Zend 虚拟机编译并执行 PHP 脚本的代码,处理完成后,再将处理结果返回给 Web 服务器。这就是 PHP-FPM 的基本工作原理。

Linux 下查看 PHP-FPM 的进程情况如下,第一个是 master 进程,下面的是 worker 进程。

[nosee@instance-4 /var/log]$ ps aux | grep fpm
root       960  0.0  0.2 196284  9824 ?        Ss   10:52   0:00 php-fpm: master process (/etc/php/7.3/fpm/php-fpm.conf)
www-data   961  0.0  0.1 196284  7504 ?        S    10:52   0:00 php-fpm: pool www
www-data   962  0.0  0.1 196284  7504 ?        S    10:52   0:00 php-fpm: pool www
www-data   963  0.0  0.1 196284  7504 ?        S    10:52   0:00 php-fpm: pool www
www-data   964  0.0  0.1 196284  7504 ?        S    10:52   0:00 php-fpm: pool www
www-data   965  0.0  0.1 196284  7508 ?        S    10:52   0:00 php-fpm: pool www

详细步骤:
1)Web 服务器(Nginx)启动时载入 FastCGI 进程管理器(PHP-FPM),FastCGI 进程管理器自身初始化,启动多个 CGI 解释器进程(php-cgi),并等待来自 Web Server 的连接。
2)当收到 Web 服务器请求时,FastCGI 进程管理器选择并连接到一个 CGI 解释器。Web 服务器将 CGI 环境变量和标准输入发送到 FastCGI 子进程 php-cgi。
3)FastCGI 子进程完成处理后,将标准输出和错误信息从同一连接返回给 Web 服务器。当 FastCGI 子进程关闭连接时,即该请求处理完成。(在CGI模式中,php-cgi 在此便退出了)
4)FastCGI 子进程接着等待,并处理来自 FastCGI 进程管理器的下一个连接。

FastCGI 像是一个常驻(long-live)型的 CGI,它可以一直执行着,只要激活后,不会每次都要花费时间去 fork 一次。它还支持分布式的运算, 即 FastCGI 程序可以在网站服务器以外的主机上执行,并且接受来自其它网站服务器来的请求。

FastCGI 是语言无关的、可伸缩架构的 CGI 开放扩展,其主要行为是将 CGI 进程保持在内存中,并因此获得较高的性能。众所周知,CGI 程序的反复加载是 CGI 性能低下的主要原因,如果CGI进程保持在内存中,并接受 FastCGI 进程管理器调度,则可以提供良好的性能、伸缩性、Fail-Over 特性等等。

四、CGI 与 FastCGI 的区别

1)CGI 模式中,直接杀死 php-cgi 进程,php就不能运行了。FastCGI 模式就没有这个问题,守护进程会平滑重新生成新的 php-cgi 子进程。)
2)对于 CGI 来说,每一个 Web 请求 PHP 都必须重新解析 php.ini、重新载入全部扩展,并重新初始化全部数据结构。而使用 FastCGI,所有这些都只在进程启动时发生一次。一个额外的好处是,持续数据库连接(Persistent database connection)可以工作。
3)修改 php.ini 之后,php-cgi 进程无法平滑重启。php-fpm 对此的处理机制是新的 worker 用新的配置,已经存在的 worker 处理完手上的活就不再接受新的请求并准备退出,通过这种机制来平滑过度。
4)由于 FastCGI 是多进程,所以比 CGI 消耗更多的服务器内存。如每个 php-cgi 解释器进程消耗 7MB 至 25MB 内存,将这个数字乘以 50 或 100 就是很大的内存数了。

为了方便理解,以下是对相关概念的简单介绍:

  • 接口协议 SAPI
    CGI:(Common Gateway Interface)通用网关接口,是 Web Server 与 Web Application 之间数据交换的一种协议。
    FastCGI:(Fast Common Gateway Interface)快速通用网关接口。同 CGI,是一种通信协议,但比 CGI 在效率上做了一些优化。
  • 接口程序
    PHP-CGI:是 PHP 对 Web Server 提供的 CGI 协议的接口程序。
    PHP-FPM(FastCGI Process Manager):FastCGI 进程管理器,是 PHP 对 Web Server 提供的 FastCGI 协议的接口程序,额外还提供了相对智能一些任务管理。
  • Web Server
    一般指 pache、Nginx、IIS、Lighttpd、Tomcat 等服务器。

五、参考

官方:
针对服务器的扩展:https://www.php.net/manual/zh/refs.utilspec.server.php
FastCGI 进程管理器(FPM):https://www.php.net/manual/zh/install.fpm.php
PHP 的命令行模式:https://www.php.net/manual/zh/features.commandline.php
php_sapi_name 函数:https://www.php.net/manual/zh/function.php-sapi-name.php

程序猿专栏:
Fast CGI 工作原理:https://mp.weixin.qq.com/s/Osag3QNs5u9H3jqQzsWy6A
nginx+php-fpm工作原理:https://mp.weixin.qq.com/s/-7YByKb9DYdQNqWZ11KIfA
全面了解CGI、FastCGI、PHP-FPM:https://mp.weixin.qq.com/s/BHYQNDcXXXr7U04lWwQdrg
避免PHP-FPM内存泄漏导致内存耗尽:https://mp.weixin.qq.com/s/QGa-p9bxiWKCdK0nByWLZw
php-fpm的reload过程:https://mp.weixin.qq.com/s/Cc4F9sh8DtdIpoJqXUp4rA
对于php-fpm和cgi,还有并发响应的理解:https://mp.weixin.qq.com/s/OQCpFZJWSuEGiog-t2zMSQ
深入理解PHP之:Nginx 与 FPM 的工作机制:https://mp.weixin.qq.com/s/uwNusOaWr7RSQD3LlmuS2w

其它:
CGI综述:https://www.cnblogs.com/thinksasa/p/4497567.html
php五大运行模式CGI,FAST-CGI,CLI,ISAPI,APACHE模式浅谈:https://www.phpernote.com/news/723.html
SSRF漏洞之FastCGI利用篇:https://blog.csdn.net/weixin_39664643/article/details/114977217
平滑重启更新(GR机制):https://www.cnblogs.com/frankltf/p/8862492.html
nginx.conf中的fastcgi_pass:https://blog.csdn.net/afring/article/details/93880216
PHP-FPM进程数的设定:https://www.cnblogs.com/ahaii/p/5776809.html

书籍:
《PHP7内核剖析》、《Modern PHP》

你可能感兴趣的:(PHP底层原理:CGI、FastCGI与PHP-FPM)