浅谈PHP

来源:互联网 发布:有什么书关于java snmp 编辑:程序博客网 时间:2024/06/04 18:51

        这篇博客分三个部分陈述,分别是PHP进程管理器、PHP启动流程和PHP优化。


        谈到PHP进程管理器,不得不讲下它的进化过程,CGI--->FastCGI--->PHP-FPM。


        CGI全称是“公共网关接口”(Common Gateway Interface),是外部应用程序(CGI程序)与Web服务器之间的接口标准。


        以APP调用CRM服务接口为例,当web服务器收到此请求后,根据配置文件知道这不是一个静态文件,需要去找PHP解释器来处理。于是,web服务器会把这个请求经过相关处理后交给PHP解释器。接下来PHP解释器会解析php.ini文件,初始化执行环境,然后处理请求,再以CGI规定的格式返回处理后的结果,退出进程。web服务器再把结果返回给客户端。


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


        FastCGI是一个可伸缩地、高速地在HTTP server和动态脚本语言间通信的接口, 是CGI的一种改进方案。与传统的CGI不用的是,FastCGI像是一个常驻(long-live)型的CGI,它可以一直执行着,只要激活后,不会每次都要花费时间去fork一次。具体表现为:FastCGI会先启一个master,解析配置文件,初始化执行环境,然后再启动多个worker。当请求过来时,master会传递给一个worker,然后立即可以接受下一个请求。这样就避免了重复的劳动,效率自然是高。


        PHP-FPM是一个实现了FastCGI的程序,PHP5.3.3已经集成php-fpm了,不再是第三方的包了,在./configure的时候带 –enable-fpm参数即可开启PHP-FPM。PHP-FPM提供了更好的PHP进程管理方式,可以有效控制内存和进程、可以平滑重载PHP配置。


       PHP-FPM进程管理器自身初始化,启动多个CGI解释器进程等待来自Nginx的请求。当客户端请求达到PHP-FPM,管理器选择到一个CGI进程进行处理,Nginx将CGI环境变量和标准输入发送到一个PHP-CGI子进程。PHP-CGI子进程处理完成后,将标准输出返回给Nginx,当PHP-CGI子进程关闭连接时,请求处理完成。PHP-CGI子进程等待着下一个连接。


        下面可以看看PHP-FPM的常用配置。

        

;listen = 127.0.0.1:9000 nignx与php-fpm通讯地址;修改之后会有502错误,fastcgi错误listen = /dev/shm/php-cgi.socklocation ~ .*\.(php|php5)?$  {    #fastcgi_pass  127.0.0.1:9000;    #/dev/shm/是linux下一个非常有用的目录,因为这个目录不在硬盘上,而是在内存里。    fastcgi_pass   unix:/dev/shm/php-cgi.sock;    fastcgi_index index.php;    include fastcgi.conf;};动静子进程pm = dynamic/static:进程管理pm.max_children:static模式下创建的子进程数或dynamic模式下同一时刻允许最大的php-fpm子进程数量pm.start_servers:动态方式下的起始php-fpm进程数量pm.min_spare_servers:动态方式下服务器空闲时最小php-fpm进程数量pm.max_spare_servers:动态方式下服务器空闲时最大php-fpm进程数量pm.max_requests:php-fpm子进程能处理的最大请求数;指定了一个php-fpm子进程执行多少次之后重启该进程,nginx会报502

        PHP启动过程分两部分讲,SAPI和PHP生命周期。


        SAPI是Server Application Programming Interface(服务器应用编程接口)的缩写。PHP通过SAPI提供了一组接口,供应用和PHP内核之间进行数据交互。PHP提供了一个函数查看当前SAPI接口类型:php_sapi_name(cli/cli-server 命令行模式/apache2handler Apache加载PHP以模块模式/fastcgi Nignx+FastCGI 模式)。


        


        生命周期:无论使用哪种SAPI,在PHP执行脚本前后,都包含一系列事件:Module的Init(MINT)和Shutdown(MSHUTDOWN),Request 的Init(RINT)和Shutdown(RSHUTDOWN)。




        第一阶段是PHP模块初始化阶段(MINT),可以初始化扩展内部变量、分配资源和注册资源处理器,在整个PHP实例生命周期内,该过程只执行一次。使用get_loaded_extensions 函数来查看所有编译并加载的模块/扩展,相当于CLI模式下的php -m。


        第二阶段是请求初始化阶段(RINT),在模块初始化并激活后,会创建PHP运行环境,同时调用所有模块注册的RINT函数,调用每个扩展的请求初始化函数 ,设定特定的环境变量、分配资源或执行其他任务,如审核。


        第三阶段,请求处理完成后,会调用PHP_RSHUTDOWN_FUNCTION进行回收,这是每个扩展的请求关闭函数,执行最后的清理工作。Zend引擎执行清理过程、垃圾收集、对之前的请求期间用到的每个变量执行unset。请求完成可能是执行到脚本完成,也可能是调用die()或exit()函数完成。

        第四阶段,当PHP生命周期结束时候,PHP_MSHUTDOWN_FUNCTION对模块进行回收处理,这是每个扩展的模块关闭函数,用于关闭自己的内核子系统。

        

单进程下PHP生命周期



多进程下PHP生命周期



多线程下PHP生命周期


        最后,谈谈PHP优化,分为zval结构、opcache和代码优化三部分。


        先介绍下PHP中变量及其内存对象的内部表示。PHP变量划分为两类:标量类型和复杂类型。标量类型包括布尔型、整型、浮点型和字符串;复杂类型包括数组、对象和资源;还有一个NULL比较特殊,它不划分为任何类型,而是单独成为一类。所有这些类型,在PHP内部统一用一个叫做zval的结构表示,在PHP源代码中这个结构名称为“_zval_struct”。zval的具体定义在PHP源代码的“Zend/zend.h”文件中。




        其中联合体“_zvalue_value”用于表示PHP中所有变量的值,这里之所以使用union,是因为一个zval在一个时刻只能表示一种类型的变量。可以看到_zvalue_value中只有5个字段,但是PHP中算上NULL有8种数据类型,那么PHP内部是如何用5个字段表示8种类型呢?这算是PHP设计比较巧妙的一个地方,它通过复用字段达到了减少字段的目的。例如,在PHP内部布尔型、整型及资源(只要存储资源的标识符即可)都是通过lval字段存储的;dval用于存储浮点型;str存储字符串;ht存储数组(注意PHP中的数组其实是哈希表);而obj存储对象类型;如果所有字段全部置为0或NULL则表示PHP中的NULL,这样就达到了用5个字段存储8种类型的值。




        值得一提的是,PHP5中zval占24个字节,而PHP7中只占16个字节,PHP RC7已于本月12号放出,本来是放正式版本的,此版本性能比PHP5提升一倍,值得期待。



PHP7的zval结构



PHP7的性能测试结果,性能压测结果,耗时从2.991下降到1.186,大幅度下降60%。


        PHP代码经过编译后,会生成opcache码,由Zend引擎执行。由于PHP是解释型语言,每次执行PHP都会重新编译,这会造成一定得性能损耗。那么,把opcache存储起来,岂不是更快了。Alternative PHP Cache (APC) 是一个开放自由的PHP opcode 缓存。它的目标是提供一个自由、 开放,和健全的框架用于缓存和优化PHP的中间代码。





        说起PHP优化来,有两大法宝,基本上不用改什么代码,性能就蹭蹭提升了很多,法宝为PHP版本升级和opcache,示例可以见上面两幅图。


        目前主流的框架大多是php代码写的,这里推荐个C扩展的框架,Yaf(Yet another framework),PHP启动的时候便加载到内存,速度性能远比PHP写的框架强很多。此框架由目前国内最权威的PHP专家鸟哥开发维护,性能没的说,在老东家微博工作时就用的是此框架,也可以见鸟哥关于此框架的Yaf的性能对比测试博客。


        善用xhprof或xdebug查看PHP代码中的性能瓶颈。


        下面就罗列了下,PHP的主要优化写法

1、尽可能的使用PHP内部函数2、能用PHP内部字符串操作函数的情况下,尽量用他们,不要用正则表达式; 因为其效率高于正则3、在循环之前设置循环的最大次数,而非在在循环中4、尽量使用单引号5、在组织大字符串时多试试heredoc、nowdoc写法6、最好不用@,用@掩盖错误会降低脚本运行速度7、用单引号代替双引号来包含字符串,这样做会更快一些8、使用++$i递增9、不要随便就复制变量,会增加内存消耗10、循环里不要套SQL查询,SQL查询时不要用MySQL自带的函数,交给PHP来处理11、使用xhprof或xdebug查看性能12、缓存,Memcached MongoDB Redis13、……


        【ps】此博客由PPT整理而成,插图均由网上搜集,文章内容部分也参照网上优秀文章而来。


0 0
原创粉丝点击