美文网首页
一次请求的完整生命周期

一次请求的完整生命周期

作者: fujun_195a | 来源:发表于2018-01-03 18:35 被阅读0次

    目录

    1、基本概念

    什么是CGI?

    部分专业术语解释

    图解静态请求和动态请求

    php组成

    2、PHP的生命周期

    3、PHP底层工作原理

    基本概念

    1.什么是CGI?

    1)什么是cgi呢? CGI是common gateway interface的缩写,译作通用网关接口,但很不幸,我们无法见名知意,我们知道,web服务器所处理的内容都是静态的,要想处理动态内容,需要依赖于web应用程序,如php、jsp、python、perl等。但是web server如何将动态的请求传递给这些应用程序?它所依赖的就是cgi协议。没错,是协议,也就是web server和web应用程序交流时的规范。换句话说,通过cgi协议,再结合已搭建好的web应用程序,就可以让web server也能"处理"动态请求,你肯定知道处理两字为什么要加上双引号。

    简单版的cgi工作方式如下:

    2.部分专业术语解释

    CGI:

    概念:它是一种协议。通过cgi协议,web server可以将动态请求和相关参数发送给专门处理动态内容的应用程序。

    工作原理:CGI方式在遇到连接请求(用户请求)先要创建cgi的子进程,激活一个CGI进程,然后处理请求,处理完后结束这个子进程。这就是fork-and-execute模式。所以用cgi方式的服务器有多少连接请求就会有多少cgi子进程,子进程反复加载是cgi性能低下的主要原因。当用户请求数量非常多时,会大量挤占系统的资源如内存,CPU时间等,造成效能低下。

    2)FASTCGI:

    概念:也是一种协议,只不过是cgi的优化版。cgi的性能较烂,fastcgi则在其基础上进行了改进。FastCGI技术目前支持语言有:C/C++、Java、Perl、Tcl、Python、SmallTalk、Ruby等

    工作原理:fast-cgi是cgi的升级版本,是一个进程可以处理多个请求,和上面的cgi协议完全不一样,cgi是一个进程只能处理一个请求,这样就会导致大量的cgi程序,因此会给服务器带来负担。FastCGI像是一个常驻型的CGI,它可以一直执行着,只要激活后,不会每次都要花费时间去fork一次

    3)PHP-CGI:

    概念:PHP-CGI是PHP自带的FastCGI管理器。

    工作原理:fastcgi是一种协议,而php-cgi实现了这种协议。不过这种实现比较烂。它是单进程的,一个进程处理一个请求,处理结束后进程就销毁。

    4)PHP-FPM:

    概念:PHP-FPM是一个PHP FastCGI管理器,是只用于PHP的是对php-cgi的改进版,它直接管理多个php-cgi进程/线程。也就是说,php-fpm是php-cgi的进程管理器因此它也算是fastcgi协议的实现。

    工作原理:php-fpm会开启多个php-cgi程序,并且php-fpm常驻内存,每次web serve服务器发送连接过来的时候,php-fpm将连接信息分配给下面其中的一个子程序php-cgi进行处理,处理完毕这个php-cgi并不会关闭,而是继续等待下一个连接(一个进程至少服务上万次请求才退出),这也是fast-cgi加速的原理,但是由于php-fpm是多进程的,而一个php-cgi基本消耗7-25M内存,因此如果连接过多就会导致内存消耗过大,引发一些问题,例如nginx里的502错误。

    为什么一定要退出?因为担心RINIT->RSHUTDOWN循环,有哪个代码写的不好,变量一直没释放,内存泄露GC又回收不了。php-fpm里的pm.max_requests配置就是设置RINT循环多少次,退出进程。

    同时php-fpm还附带一些其他的功能:例如平滑过渡配置更改,普通的php-cgi在每次更改配置后,需要重新启动才能初始化新的配置,而php-fpm是不需要,php-fpm分将新的连接发送给新的子程序php-cgi,这个时候加载的是新的配置,而原先正在运行的php-cgi还是使用的原先的配置,等到这个连接后下一次连接的时候会使用新的配置初始化,这就是平滑过渡

    5)CGI进程/线程

    概念:在php上,就是php-cgi进程/线程。专门用于接收web server的动态请求,调用并初始化zend虚拟机。

    6)CGI脚本:

    概念:被执行的php源代码文件。

    7)ZEND虚拟机:

    概念:对php文件做词法分析、语法分析、编译成opcode,并执行。最后关闭zend虚拟机。

    CGI进程/线程和ZEND虚拟机的关系:cgi进程调用并初始化zend虚拟机的各种环境。

    8)常见的SAPI

    SAPI提供了一个和外部通信的接口,常见的SAPI有:cgi 、fast-cgi、cli、isapi、apache 模块的 DLL

    3.图解静态请求和动态请求

    4.PHP的组成

    PHP总共有三个模块:内核、Zend引擎、以及扩展层。

    PHP内核用来处理请求、文件流、错误处理等相关操作。

    Zend引擎用以将源文件转换成机器语言,然后在虚拟机上运行它。

    扩展层是一组函数、类库和流,PHP使用它们来执行一些特定的操作。

    比如,我们需要mysql扩展来连接MySQL数据库; 当ZE执行程序时可能会需要连接若干扩展,这时ZE将控制权交给扩展,等处理完特定任务后再返还;最后,ZE将程序运行结果返回给PHP内核,它再将结果传送给SAPI层,最终输出到浏览器上。

    5、WEB-SERVER和PHP-CGI的交互模式

    web server和php-cgi有3种交互模式。

    cgi模式:httpd接收到一个动态请求就fork一个cgi进程,cgi进程返回结果给httpd进程后自我销毁。

    动态模块模式:将php-cgi的模块编译进httpd(Apache的方法)。在httpd启动时会加载模块,加载时也将对应的模块激活,php-cgi也就启动了。(注:纠正一个小小错误,很多人以为动态编译的模块是可以在需要的时候随时加载调用,不需要的时候它们就停止了,实际上不是这样的。和静态编译的模块一样,动态加载的模块在被加载时就被加入到激活链表中,无论是否使用它,它都已经运行在apache httpd的内部)

    php-fpm模式:使用php-fpm管理php-cgi,此时httpd不再控制php-cgi进程的启动。可以将php-fpm独立运行在非web服务器上,实现所谓的动静分离。

    实际上,借助模块mod_fastcgi还可以实现fastcgi模式。同cgi一样,管理模式的先天缺陷决定了这并不是一种好方法。

    2、PHP的生命周期

    流程图解:

    PHP开始和结束阶段:

    由上面的图,我们能知道,SAPI运行PHP都经过下面几个阶段

    模块初始化阶段(Module init):即调用每个拓展源码中的的PHP_MINIT_FUNCTION中的方法初始化模块,进行一些模块所需变量的申请,内存分配等。

    请求初始化阶段(Request init):即接受到客户端的请求后调用每个拓展的PHP_RINIT_FUNCTION中的方法,初始化PHP脚本的执行环境。

    执行PHP脚本

    请求结束(Request Shutdown):这时候调用每个拓展的PHP_RSHUTDOWN_FUNCTION方法清理请求现场,并且ZE开始回收变量和内存。

    关闭模块(Module shutdown): Web服务器退出或者命令行脚本执行完毕退出会调用拓展源码中的PHP_MSHUTDOWN_FUNCTION 方法。

    我们具体来看看每一步都做了什么?

    1)模块初始化阶段

    在整个SAPI生命周期内(例如Apache启动以后的整个生命周期内或者命令行程序整个执行过程中), 该过程只进行一次,第一步的操作在任何请求到达之前就发生了。

    PHP调用各个扩展的MINIT方法,从而使这些扩展切换到可用状态。我们可以看看php.ini文件里打开了哪些扩展,这些扩展会在这个阶段进行”模块初始化“。 MINIT的意思是“模块初始化”。各个模块都定义了一组函数、类库等用以处理其他请求。

    2) 请求初始化

    当一个页面请求发生时,SAPI层将控制权交给PHP层。于是PHP设置了用于回复本次请求所需的环境变量。同时,它还建立一个变量表,用来存放执行过程 中产生的变量名和值。PHP调用各个模块的RINIT方法,即“请求初始化”。一个经典的例子是Session模块的RINIT,如果在php.ini中 启用了Session模块,那在调用该模块的RINIT时就会初始化$_SESSION变量,并将相关内容读入;RINIT方法可以看作是一个准备过程, 在程序执行之间就会自动启动。 

    3)执行脚本

    一旦请求被初始化了,ZE开始接管控制权,将PHP脚本翻译成符号(OPcode),最终形成操作码并逐步运行之。如任一操作码需要调用扩展的函数,ZE将会把参数绑定到该函数,并且临时交出控制权直到函数运行结束。

    scanner

    将PHP代码转换为Tokens,详见代码Zend/zend_language_scanner.l。

    parser

    将Tokens转换成表达式,详见代码Zend/zend_language_parser.y。

    compile

    将表达式编译成opcode。opcode存放在op_array中。

    execute

    Zend Engine调用zend_execute来执行op_array,输出结果。

    4)关闭第一步(请求关闭)

    如同PHP启动一样,PHP的关闭也分两步。一旦页面执行完毕(无论是执行到了文件末尾还是用exit或die函数中止),PHP就会启动清理程序。它会按顺序调用各个模块的RSHUTDOWN方法。 RSHUTDOWN用以清除程序运行时产生的符号表,也就是对每个变量调用unset函数。

    5)关闭第二步(模块关闭)

    所有的请求都已处理完毕,SAPI也准备关闭了,PHP开始执行第二步:PHP调用每个扩展的MSHUTDOWN方法,这是各个模块最后一次释放内存的机会。

    这样,整个PHP生命周期就结束了。要注意的是,只有在服务器没有请求的情况下才会执行“启动第一步”和“关闭第二步”。

    3、PHP底层工作原理

    从图上可以看出,php从下到上是一个4层体系

    ①Zend引擎

    Zend整体用纯c实现,是php的内核部分,它将php代码翻译(词法、语法解析等一系列编译过程)为可执行opcode的处理并实现相应的处理方法、实现了基本的数据结构(如hashtable、oo)、内存分配及管理、提供了相应的api方法供外部调用,是一切的核心,所有的外围功能均围绕zend实现。

    ②Extensions

    围绕着zend引擎,extensions通过组件式的方式提供各种基础服务,我们常见的各种内置函数(如array系列)、标准库等都是通过extension来实现,用户也可以根据需要实现自己的extension以达到功能扩展、性能优化等目的(如贴吧正在使用的php中间层、富文本解析就是extension的典型应用)。

    ③Sapi

    Sapi全称是Server Application Programming Interface,也就是服务端应用编程接口,sapi通过一系列钩子函数,使得php可以和外围交互数据,这是php非常优雅和成功的一个设计,通过sapi成功的将php本身和上层应用解耦隔离,php可以不再考虑如何针对不同应用进行兼容,而应用本身也可以针对自己的特点实现不同的处理方式。后面将在sapi章节中介绍

    ④上层应用

    这就是我们平时编写的php程序,通过不同的sapi方式得到各种各样的应用模式,如通过webserver实现web应用、在命令行下以脚本方式运行等等。

    构架思想:

    引擎(Zend)+组件(ext)的模式降低内部耦合

    中间层(sapi)隔绝web server和php

    相关文章

      网友评论

          本文标题:一次请求的完整生命周期

          本文链接:https://www.haomeiwen.com/subject/bttrkxtx.html