🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
> **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## <h5>CGI介绍</h5> 最早的Web服务器只是处理浏览器发来的HTTP静态资源请求(图片、视频、HTML文件),就比如说浏览器需要一张图片,Web服务器找到之后直接返回给浏览器。 但随着技术的不断发展,Web服务器也需要接收浏览器对于动态资源的请求,但对于像PHP/ASP这些文件Web服务器无法直接处理,就需要转给这些文件能处理的工具处理。解释器就可以理解为处理这些文件的工具,而CGI程序就相当于最初的解释器。 CGI(common gateway interface 公共网关接口)起初是web服务器跟解释器之间的通信协议,即两者遵循CGI协议进行数据传输。后来CGI代指遵循CGI协议的解释器,也就是CGI程序。 <h5>CGI程序的工作方式</h5> web服务器一般只处理静态文件请求(如 jpg、htm、html),如果碰到一个动态脚本请求(如php),web 服务器主进程,就fork 出一个新的进程来启动CGI程序。也就是说将动态脚本请求交给CGI程序来处理。启动CGI程序需要一个过程,比如,读取配置文件,加载扩展等。CGI程序启动后,就会解析动态脚本,然后将结果返回给web服务器。最后web服务器再讲记过返回给客户端,刚才fork的进程也会随之关闭。这样,每次用户请求动态脚本,web服务器都要重新fork一个新进程,将配置加载一遍,然后去启动CGI程序,由CGI程序来处理动态脚本,处理完后进程随之关闭。CGI模式的运行示意图如下: ![cgi工作原理][1] ##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服务器内置模块][2] ##FastCGI## FastCGI协议在CGI标准协议基础上改进出来的一种协议,主要就是解决CGI每次都要Fork出子进程去加载CGI程序的问题。当客户端请求web服务器上的动态脚本时,Web服务器会将动态脚本交给FastCGI主进程,FastCGI主进程根据情况,安排一个空闲的子进程来解释动态脚本,处理完成后将结果返回给web服务器,web服务器再将结果返回给客户端。客户端请求处理完毕后,FastCGI子进程并不会随之关闭,而是继续等待主进程安排工作任务。所以,FastCGI的工作效率是非常高的。 FastCGI的相关定义文章:[FastCGI Specification][3] [the FastCGI Interface][4] FastCGI模式运行示意图如下: ![请输入图片描述][5] ##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官方集成了。 <h5>PHP-FPM的优点</h5> <li>支持平滑停止/启动的高级进程管理功能;</li> <li>可以工作于不同的 uid/gid/chroot 环境下,并监听不同的端口和使用不同的 php.ini 配置文件(可取代 safe_mode 的设置);</li> <li>stdout 和 stderr 日志记录;</li> <li>在发生意外情况的时候能够重新启动并缓存被破坏的 opcode;</li> <li>文件上传优化支持;</li> <li>&#8220;慢日志&#8221; &#8211; 记录脚本(不仅记录文件名,还记录 PHP backtrace 信息,可以使用 ptrace或者类似工具读取和分析远程进程的运行数据)运行所导致的异常缓慢;</li> <li>fastcgi_finish_request() &#8211; 特殊功能:用于在请求完成和刷新数据后,继续在后台执行耗时的工作(录入视频转换、统计处理等);</li> <li>动态/静态子进程产生;</li> <li>基本 SAPI 运行状态信息(类似Apache的 mod_status);</li> <li>基于 php.ini 的配置文件。</li> <h5>进程管理模式</h5> 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 <h5>PHP-FPM的进程管理方式</h5> 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> <h5>php-fpm平滑重启原理</h5> <p>因为php-fpm本身并没有类似nginx的<code>reload</code>之类的命令,你用<code>man php-fpm</code>也能看出来,确实没有,如果你有在网上看到过<code>php-fpm reload</code>或<code>service php-fpm reload</code>之类的命令或者你使用过,它本身并不是<code>php-fpm</code>,而是一个shell脚本而已,它所在的位置有在<code>/etc/init.d/php-fpm</code>,而这个<code>reload</code>的本质,其实就是给php-fpm主进程发送<code>-SIGUSR2</code>信号,而php-fpm规定了<code>-SIGUSR2</code>信号为平滑重启信号(参见<a href="https://www.jianshu.com/p/2ea78b789263">php-fpm信号,你造么?</a>)。<br /> <img src="https://img.xiebruce.top/2019/05/30/72d674e28c281dc028969c09363f6876.jpg" alt="Xnip2019-05-30_19-12-09.jpg" /></p> <p><strong>平滑重启步骤:</strong><br /> &#8211; 1、master通过给子进程发送SIGQUIT信号的方式,平滑关闭所有的子进程<br /> &#8211; 2、如果过一段时间,有些子进程还没退出,给子进程发送SIGTERM信号,强制关闭子进程<br /> &#8211; 3、如果还没关闭,给子进程发送SIGKILL信号,强制关闭<br /> &#8211; 4、等所有的子进程退出后,master重新启动</p> <h5>扩展文章</h5> <a href="https://mengkang.net/668.html">深入理解 FastCGI 协议以及在 PHP 中的实现</a><br /> <a href="https://segmentfault.com/q/1010000000256516">搞不清FastCgi与PHP-fpm之间是个什么样的关系</a><br /> <a href="https://www.jianshu.com/p/c9a028c834ff">php-fpm解读-进程管理的三种模式</a></p> ## 结尾 ## <p style="background-image: -webkit-linear-gradient(left, #3498db, #f47920 10%, #d71345 20%, #f7acbc 30%,#ffd400 40%, #3498db 50%, #f47920 60%, #d71345 70%, #f7acbc 80%, #ffd400 90%, #3498db);color: transparent;-webkit-text-fill-color: transparent;-webkit-background-clip: text;text-align:center;"> 腹有诗书气自华,最是书香能致远。 </p> [1]: https://blog.zxliu.cn/usr/uploads/2020/12/3444062762.jpg [2]: https://blog.zxliu.cn/usr/uploads/2020/12/1765529164.jpg [3]: http://www.mit.edu/~yandros/doc/specs/fcgi-spec.html#S1 [4]: http://www.mit.edu/afs.new/sipb/project/merakidev/src/openwrt-meraki/openwrt/build_mips/lighttpd-1.4.11/doc/fastcgi.txt [5]: https://blog.zxliu.cn/usr/uploads/2020/12/3668045550.jpg