CGI FsatCGI PHP-FPM PHP-CGI之间的联系

CGI FsatCGI PHP-FPM PHP-CGI之间的联系

说明

算是面试必问的题吧,以前对这块理解比较模糊,花时间看了一下,整理下来


文章参考

Bruce's Blog: https://www.xiebruce.top/606.html
百度文库: https://wenku.baidu.com/view/94a635b45901020207409cfe.html
博客园:https://www.cnblogs.com/itxiongwei/p/9072075.html
知乎:https://www.zhihu.com/question/55835080

CGI(公共网关接口):CGI最初是一种外部扩展应用程序与 Web 服务器通信的协议,现在常说的就是实现了CGI协议的程序。

FsatCGI:FastCGI则是在CGI基础上的优化。CGI在高访问量下效率会变得很低(这个问题会在下文中详细给出),而FastCGI则完美的解决了这个问题。目前FsatCGI是通用的公共网管接口协议。

PHP-FPM:FPM是 FastCgi Process Manager的缩写,即FastCGI的进程管理器,而PHP-FPM就是PHP的FastCGI进程管理器。

PHP-CGI:PHP-CGI同样也是实现了FastCGI协议的PHP解释器。只不过在5.4以后,PHP-FPM已经被集成在了PHP里,PHP-CGI就慢慢地被遗弃了。

CGI

CGI介绍

最早的Web服务器只是处理浏览器发来的HTTP静态资源请求(图片、视频、HTML文件),就比如说浏览器需要一张图片,Web服务器找到之后直接返回给浏览器。
但随着技术的不断发展,Web服务器也需要接收浏览器对于动态资源的请求,但对于像PHP/ASP这些文件Web服务器无法直接处理,就需要转给这些文件能处理的工具处理。解释器就可以理解为处理这些文件的工具,而CGI程序就相当于最初的解释器。
CGI(common gateway interface 公共网关接口)起初是web服务器跟解释器之间的通信协议,即两者遵循CGI协议进行数据传输。后来CGI代指遵循CGI协议的解释器,也就是CGI程序。

CGI程序的工作方式

web服务器一般只处理静态文件请求(如 jpg、htm、html),如果碰到一个动态脚本请求(如php),web 服务器主进程,就fork 出一个新的进程来启动CGI程序。也就是说将动态脚本请求交给CGI程序来处理。启动CGI程序需要一个过程,比如,读取配置文件,加载扩展等。CGI程序启动后,就会解析动态脚本,然后将结果返回给web服务器。最后web服务器再讲记过返回给客户端,刚才fork的进程也会随之关闭。这样,每次用户请求动态脚本,web服务器都要重新fork一个新进程,将配置加载一遍,然后去启动CGI程序,由CGI程序来处理动态脚本,处理完后进程随之关闭。CGI模式的运行示意图如下:

cgi工作原理

Web服务器内置模块

对于CGI模式来说,每一个请求都需要fork出一个进程去重启加载CGI程序,这中模式效率其实很低下。Web服务器内置模块则是比CGI高效的一种方式。例如,Apache的mod_php模块,将PHP解释器做成模块,加载到apache服务器中。在apache服务器在启动的时候,就会启动PHP模块。当客户端请求PHP文件是,Apache服务器不再fork出新的进程来启动PHP解释器,而是直接将PHP文件交给运行中的php模块处理,显然,这种方式下,效率比较高。

由于在Apache服务器启东时,才会读取php的配置文件的,加载PHP模块,在Apache的运行过程中,不会再重新读取php的配置文件,所以,每次我们修改了PHP的配置文件后,必须重启apache,新的PHP配置文件才会生效,Web服务器内置模块运行示意图如下:

web服务器内置模块

FastCGI

FastCGI协议在CGI标准协议基础上改进出来的一种协议,主要就是解决CGI每次都要Fork出子进程去加载CGI程序的问题。当客户端请求web服务器上的动态脚本时,Web服务器会将动态脚本交给FastCGI主进程,FastCGI主进程根据情况,安排一个空闲的子进程来解释动态脚本,处理完成后将结果返回给web服务器,web服务器再将结果返回给客户端。客户端请求处理完毕后,FastCGI子进程并不会随之关闭,而是继续等待主进程安排工作任务。所以,FastCGI的工作效率是非常高的。
FastCGI的相关定义文章:FastCGI Specification the FastCGI Interface

FastCGI模式运行示意图如下:
请输入图片描述

PHP-CGI

PHP-CGI是遵循CGI协议的PHP解释器,在5.4之前PHP_FPM通过PHP-CGI来解析PHP文件,但5.4之后,PHP-FPM已经被官方收录,可以更高效的解析PHP。现在的PHP-CGI也同样遵循FastCGI协议,只不过大家都在用PHP-FPM,PHP-CGI慢慢的被遗弃了。

PHP-FPM

FPM 是FastCGI Process Manager的缩写,中文直译就是FastCGI进程的管理器,而PHP-FPM就是PHP语言的FastCGI进程管理器。对于PHP5.4之前的版本来说,PHP-FPM是一个第三方的补丁包,PHP_FPM通过PHP-CGI来解析PHP文件。但在PHP5.4以后的版本里,PHP-FPM已经被集成到PHP的源码中了,因为PHP-FPM提供了更好的PHP进程管理方式,可以有效控制内存和进程、可以平滑重载PHP配置,比spawn-fcgi具有更多优点,所以PHP-FPM被PHP官方集成了。

PHP-FPM的优点

<li>支持平滑停止/启动的高级进程管理功能;</li>
<li>可以工作于不同的 uid/gid/chroot 环境下,并监听不同的端口和使用不同的 php.ini 配置文件(可取代 safe_mode 的设置);</li>
<li>stdout 和 stderr 日志记录;</li>
<li>在发生意外情况的时候能够重新启动并缓存被破坏的 opcode;</li>
<li>文件上传优化支持;</li>
<li>“慢日志” – 记录脚本(不仅记录文件名,还记录 PHP backtrace 信息,可以使用 ptrace或者类似工具读取和分析远程进程的运行数据)运行所导致的异常缓慢;</li>
<li>fastcgi_finish_request() – 特殊功能:用于在请求完成和刷新数据后,继续在后台执行耗时的工作(录入视频转换、统计处理等);</li>
<li>动态/静态子进程产生;</li>
<li>基本 SAPI 运行状态信息(类似Apache的 mod_status);</li>
<li>基于 php.ini 的配置文件。</li>

进程管理模式

PHP-FPM是一个进程管理器,启动它时,它除了有一个“主进程(master)”外,还会创建很多“子进程”。这些子进程,才是真正的“遵循通用网关接口的php解释器”,而主进程只不过是把请求分配给这些子进程而已,所以PHP-FPM才叫“PHP FastCGI进程管理器”,当网站并发数大时,主进程会不断把请求分配给这些子进程,从而可以同时处理高并发而不“挂掉”。

另外,子进程的多少,主进程还会“自动分配”,比如并发数大时,主进程会多创建一些子进程,用于同时处理更多的请求,而当并发数小时,则会自动关闭一些进程,从而“减少这些子进程对服务器内存的占用”,当然这个取决于PHP-FPM配置的进程管理方式(static方式不会自动)。

➜  ~  ps -fe |grep "php-fpm"
  502   360     1   0 25Nov20 ??         0:17.52 /usr/local/opt/php@7.2/sbin/php-fpm --nodaemonize
  502 21971   360   0 Thu02PM ??         0:00.62 /usr/local/opt/php@7.2/sbin/php-fpm --nodaemonize
  502 22233   360   0 Thu02PM ??         0:01.33 /usr/local/opt/php@7.2/sbin/php-fpm --nodaemonize
  502 22234   360   0 Thu02PM ??         0:00.52 /usr/local/opt/php@7.2/sbin/php-fpm --nodaemonize
  502 31928   584   0  5:17PM ttys003    0:00.00 grep --color=auto --exclude-dir=.bzr --exclude-dir=CVS --exclude-dir=.git --exclude-dir=.hg --exclude-dir=.svn --exclude-dir=.idea --exclude-dir=.tox php-fpm

PHP-FPM的进程管理方式

PHP-FPM的进程管理方式有三种

<li>static:静态方式,即子进程数是固定的,不会随着并发数的多少而自动调整子进程数,有两个缺点,1、当并发数少时,如果子进程太多会浪费内存,2、当并发数大时也不会自动增加子进程,比如“死板”,我们一般不会用这种方式。</li>
<li>dynamic:动态分配,当空闲时,会自动缩小到最少子进程数(通过pm.min_spare_servers指定),当并发数大时,会按需求增加子进程数,当然这个增加并不是无止境的,而是有最大子进程数的(通过pm.max_children指定,另外也有空闲时的最大子进程数,通过pm.max_spare_servers指定),一般我们都采用这种进程管理方式。</li>
<li>ondemand:启动php-fpm时,只有主进程,没有子进程,当有请求过来时,才会创建子进程,并发数越多创建的子进程数就越多,但有个极限值(由pm.max_children指定),空闲的进程会在pm.process_idle_timeout秒内被关闭,这种方式无法及时的“响应并处理”nginx的请求,虽然这会让php-fpm在空闲时占用内存最小,但没有必要,因为服务器不缺这点内存,这种方式一般也不会使用。</li>
</ul>

php-fpm平滑重启原理

<p>因为php-fpm本身并没有类似nginx的reload之类的命令,你用man php-fpm也能看出来,确实没有,如果你有在网上看到过php-fpm reloadservice php-fpm reload之类的命令或者你使用过,它本身并不是php-fpm,而是一个shell脚本而已,它所在的位置有在/etc/init.d/php-fpm,而这个reload的本质,其实就是给php-fpm主进程发送-SIGUSR2信号,而php-fpm规定了-SIGUSR2信号为平滑重启信号(参见php-fpm信号,你造么?)。

Xnip2019-05-30_19-12-09.jpg</p>
<p>平滑重启步骤:

– 1、master通过给子进程发送SIGQUIT信号的方式,平滑关闭所有的子进程

– 2、如果过一段时间,有些子进程还没退出,给子进程发送SIGTERM信号,强制关闭子进程

– 3、如果还没关闭,给子进程发送SIGKILL信号,强制关闭

– 4、等所有的子进程退出后,master重新启动</p>

扩展文章

深入理解 FastCGI 协议以及在 PHP 中的实现

搞不清FastCgi与PHP-fpm之间是个什么样的关系

php-fpm解读-进程管理的三种模式</p>

结尾

腹有诗书气自华,最是书香能致远。

添加新评论