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

一次请求的完整生命周期

作者: 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

相关文章

  • 一次请求的完整生命周期

    目录 1、基本概念 什么是CGI? 部分专业术语解释 图解静态请求和动态请求 php组成 2、PHP的生命周期 3...

  • 瑞士军刀Redis

    主要内容 慢查询 生命周期 如图所示为客户端请求到Redis的完整生命周期:发送命令、排队、执行命令、返回结果 慢...

  • http

    [TOC] 1. 一次完整的http请求过程 (1).建立或处理连接 (2).接收请求 (3).处理请求 (4)....

  • 前端优化技巧

    (一)减少http请求次数或者减少请求内容的大小 每发送一次http请求,都需要完成请求+响应这个完整的http事...

  • Django:面试题记录

    1、Django请求生命周期django的生命周期是:前端请求--->nginx--->uwsgi.--->中间件...

  • 一次完整的HTTP请求

    这里讲的请求是后端DevOps可以控制的范围内,不包括DNS解析,层层的路由等等,一切都从请求到达我们自己架设的服...

  • 一次完整的http请求

    当我们在浏览器的地址栏输入 www.linux178.com ,然后回车,回车这一瞬间到看到页面到底发生了什么呢?...

  • 一次完整的网络请求

    1.域名解析 2.三次握手 3.简历tcp/id链接发起http请求 4.服务端响应 5.客户端解析服务端返回的数...

  • 一次完整的http请求

    1:建立TCP连接 2:web浏览器向web服务器发送请求行 3:web浏览器发送请求头 4:web服务器发送应答...

  • 一次完整的http请求

    一次完整的http请求的过程如下: 1、浏览器的地址栏输入URL并按下回车。 2、DNS解析。 3、根据IP建立T...

网友评论

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

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