web服务器与PHP应用之间是如何进行通信的???

通常情况下,我们一般使用Apache与Nginx作为web服务器,来部署我们的PHP应用。当用户发起一个HTTP请求的时候,PHP应用程序在处理这个请求的时候并没有直接的解析HTTP协议,而是直接从$_GET,$_POST,或者$_SERVER等全局变量中,获取到用户请求数据和其他系统环境。

为什么能够处理这个数据,是因为一个重要的东西:CGI协议。

CGI协议同HTTP协议一样是一个应用层协议,它的功能是为了解决web服务器与PHP应用(或者其他web应用)之间的通信问题。

到底CGI协议是如何进行数据通信的呢?

简单来说CGI协议描述了web服务器和应用程序之间进行数据传输的格式,并且只要我们的编程语言支持标准输入(STDIN)、标准输出(STDOUT)以及环境变量等处理,就可以使用它来编写一个CGI程序。

CGI的运行原理

  • 当用户访问我们的web应用时,会发起一个http请求。最终web服务器会接收到这个请求。

  • web服务器创建一个新的CGI进程。在这个进程中,将HTTP请求数据以一定格式解析出来,并通过标准输入和环境变量传入到URL指定的CGI程序。

  • web应用程序处理完成后将返回数据写入到标准输出中,web服务器进程则从标准输出流中读取到响应,并采用http协议返回给用户响应

一句话概括就是web服务器中的CGI进程将接收到的http请求数据读取到环境变量中,通过标准输入转发给PHP的CGI程序;当PHP程序处理完成后,web服务器中的CGI进程从标准输出中读取返回数据,并转换回http响应消息格式,最终将页面呈现给用户。然后web服务器关闭掉这个CGI进程。

CGI的缺陷

  • 每次处理用户请求,都需要重新fork CGI进程、销毁CGI进程。

  • 一系列的I/O操作降低了网络的吞吐量,造成了资源的浪费,在大并发时会产生严重的性能问题

FastCGI协议

从功能上讲,CGI协议已经能够解决web服务器与web应用之间的数据通信问题。但是由于每个请求都需要fork处CGI子进程导致性能堪忧,所以基于CGI协议的基础上做了改进便有了FastCGI协议,它是一种常驻型的CGI协议。

本质上讲FastCGI 和CGI协议几乎完全一样,他们都可以从web服务器里接收到相同的数据,不同之处在于采取了不同的通信方式。

FastCGI协议运行原理

  • FastCGI进程管理启动时会创建一个主(Master)进程和多个CGI解释器进程(Worker进程),然后等待服务器的连接。

  • web服务器接收http请求后,将CGI报文通过套接字(UNIX或TCP socket)进行通信,将环境变量和请求数据写入到标准输入,转发到CGI解释器进程。

  • CGI解释器进程完成处理后将标准输出和错误信息从同一连接返回给web服务器

  • CGI解释器进程等待下一个http请求的到来

CGI与FastCGI架构

在CGI协议中,web应用的生命周期完全依赖于http请求的声明周期。

对每个接受到的http请求,都需要重启一个CGI进程来进行处理,处理完成之后必须关闭CGI进程,才能达到通知web服务器本次http请求处理完成的目的。

但是在FastCGI中完全不一样。

FastCGI进程是常驻型的,一旦启动就可以处理所有的http请求,而无需直接退出。

再看FastCGI协议

同http协议一样,FastCGI协议也是有消息头和消息体组成。

消息头信息

  • version:用于表示FastCGI协议版本号

  • type:用于标识FastCGI消息的类型——用于指定处理这个消息的方法

  • requestid:表示出当前所属的FastCGI请求

  • content length:数据***体所占字节数

消息类型定义

  • BEGIN_REQUEST:从web服务器发送到web应用,表示开始处理新的请求。

  • ABORT_REQUEST:从web服务器发送到web应用,表示中止一个处理中的请求。比如,用户在浏览器发起请求后按下浏览器上的‘停止按钮’时,会触发这个消息。

  • END_REQUEST:从web应用发送给web服务器,表示该请求处理完成。返回数据包里包含‘返回的代码’,它决定请求是否成功处理

  • PARAMS:流数据包,从web服务器发送到web应用。此时可以发送多个数据包。发送结束标识为从web服务器发出一个长度为0的空包。且PARAMS中的数据类型和CGI协议一致。即我们使用的$_SERVER获取到的系统环境等。

  • SDTIN:流数据包,用于web应用从标准输入中读取出用户提交的数据。

  • SDTOUT:流数据包,从web应用写入到标准输出中,包含返回给用户的数据。

web服务器与FastCGI交互的过程

  • web服务器接收用户请求,但最终处理请求由web应用完成。此时,web服务器尝试通过套接字(UNIX或TCP套接字,具体使用哪个由web服务器配置决定)连接到FastCGI进程。

  • FastCGI进程查收接收到的连接。选择‘接收’或‘拒绝’连接。如果是‘接收’连接,则从标准出入流中读取数据包

  • 如果FastCGI进程在指定时间内没有成功接收到连接,则该请求失败。否则,web服务器发送一个包含唯一requestID的BEGIN_REQUEST类型消息给到FastCGI进程。后续所有数据包发送都包含这个requestID。然后,web服务器发送任意数量的PARAMS类型消息到FastCGI进程。一旦发送完毕,web服务器通过发送一个空PARAMS消息包,关闭这个流。另外,如果用户发送了POST数据web服务器会将其写入到标准输入流(SDTIN)发送给FastCGI进程,当所有的POST数据发送完成,会发送一个空的标准输入(SDTIN)来关闭这个流。

  • 同时FastCGI进程接收到BEGIN_REQUEST类型数据包。它可以通过响应END_REQUEST来拒绝这个请求。或者接收并处理这个请求。如果接收请求,FastCGI进程会等待接收所有的PARAMS和标准输入数据包。然后,在处理请求并将返回结果写入标准输出(SDTOUT)流。处理完成后,发送一个空的数据包到标准输出来关闭这个流,并且会发送一个END_REQUEST类型消息通知web服务器,告知它是否发生错误异常。

为什么需要在消息头发送requestID这个标识?

如果是每个连接仅处理一个请求,发送requestID则略显多余。

但是我们的web服务器和FastCGI进程之间的连接可能处理多个请求,即一个连接可以处理多个请求。所以才需要采用数据包协议而不是直接使用单个数据流的原因:以实现‘多路复用’。

因此,由于每个数据包都包含唯一的requestID,所以web服务器才能在一个连接上发送任意数量的请求,并且FastCGI进程也能够从一个连接上接收到任意数量的请求数据包。

另外,web服务器与FastCGI进程之间的通信是无序的。即使我们在交互过程中看起来一个请求是有序的,但是我们的web服务器也有可能在同一时间发出几十个BEGIN_REQUEST类型的数据包,以此类推。

php-fpm

php-fpm是FastCGI进程管理器(PHP FastCGI Process Manager),用于替换PHP内核的FastCGI的大部分附加功能,对与高负载网站是非常有用的。

  • 支持平滑停止/启动的高级进程管理功能

  • 可以工作于不同的uid/gid/chroot环境下,并监听不同的端口和使用不同的php.ini配置文件

  • 在发生意外情况的时候能够重亲启动并缓存被破坏的opcode

  • 文件上传优化支持

  • ‘慢日志’-记录脚本(不仅记录文件名,还记录PHP backtrace信息,可以使用ptrace或者类似工具读取和分析远程进程的运行数据)运行所导致的异常缓慢

  • fastcgi_finish_request()特殊功能:用户在请求完成和刷新数据后,继续在后台执行耗时的工作(录入视频转换、统计处理等)

  • 动态/静态子进程产生

  • 基于SAPI运行状态信息(类似Apache的mod_status)

  • 基于php.ini的配置文件

php-fpm如何工作?

php-fpm进程管理器有两种进程组成,一个master进程和多个worker进程。master进程负责监听端口,接收来自web服务器的请求,然后指派具体的worker进程处理请求;worker进程则一般多个(依据配置决定进程数),每个进程内部都嵌入了一个PHP解释器,用来执行php代码。

Nginx服务器如何与FastCGI协同工作?

Nginx服务器无法直接与FastCGI服务器进行通信,需要启用ngx_http_fastcgi_module模块进行代理配置,才能将请求发送给FastCGI服务。

配置指令:

fastcgi_pass用户设置FastCGI服务器的IP地址(TCT套接字)或UNIX套接字

fastcgi_param设置传入FastCGI服务器的参数

总结

CGI协议和FastCGI协议是一种协议和http协议一样位于应用层,与语言无关;

php-fpm是一种FastCGI协议的实现,能够管理FastCGI进程


参考文章:https://laravel-china.org/articles/14920/deeply-analyzing-the-communication-mechanism-between-web-server-and-php-application-mastering-the-operation-principle-of-cgi-and-fastcgi-protocol