这篇文章主要介绍了关于PHP内存溢出、命令行和Web服务两种执行方式的理解,有着一定的参考价值,现在分享给大家,有需要的朋友可以参考一下
开发过程中,某个接口由于从数据库读取数据量过大,返回状态为 200,但无响应数据,PHP错误日志里有如下信息:PHP Fatal error: Allowed memory size of 134217728 bytes exhausted。
很显然这是内存溢出(Out Of Memory)引发的错误,但是令我疑惑的是,Yii 框架的业务日志(application.log)里没有任何输出,页面上也没有 Stack Trace 的错误信息,于是对这个原因进行追查。
原因如下,先看 Yii 框架 CApplication.php 文件核心代码:
public function run() { if($this->hasEventHandler('onBeginRequest')) $this->onBeginRequest(new CEvent($this)); register_shutdown_function(array($this,'end'),0,false); $this->processRequest(); if($this->hasEventHandler('onEndRequest')) $this->onEndRequest(new CEvent($this)); }
在处理请求前使用了 register_shutdown_function
注册异常终止时的回调,正常来说,PHP 出现异常脚本终止时会回调 end()
方法,在 onEndRequest
事件的监听器中可以使用 error_get_last()
获取到本次错误。
但是,当 OOM 发生时,Linux Out Of Memory killer 会执行 kill -9 发送 SIGKILL 信号,根据 PHP 手册中的说明, SIGKILL 信号无法捕获和拦截,PHP 脚本会直接退出,任何清理代码都不会执行,所以 register_shutdown_function
方法不会发挥作用,自然也不会有后续的日志记录、错误页面显示等流程。
另外在开发中注意到一个现象:通过 Web 访问会出现 OOM,但通过 Console 执行就不会报错。
由此可见两种方式是有区别的,Web 访问时,PHP 脚本进程由 PHP-FPM启动,还要受 FPM 配置文件限制,/etc/php-fpm.d里配置文件有 php_admin_value[memory_limit] = 128M 限制。所以通过 Web 访问时,仅增加 php.ini 中的 memory_limit 无效。
而 Console 方式执行不经过 PHP-FPM,所以仅受 php.ini 中配置的内存参数限制,而开发机中配置的 memory_limit => 512M => 512M,所以此时不会产生 OOM。
这里有个疑问,从实现原理的角度,PHP-FPM 是如何对 PHP 进程管理的?PHP-FPM 真的会用 kill -9 杀死 PHP 脚本进程么?
附上 WebServer、PHP-FPM、PHP 脚本的调用关系:
请求首先进入 Web 服务器(如 Nginx),Nginx 分发请求(依据server节点、location节点等配置):
请求静态资源不需要 FastCGI 处理,直接转到相应文件位置
动态请求需要 PHP 代码处理,则需要把请求交给实现了 FastCGI 协议的程序(PHP-FPM)
可以在Nginx看到这样的配置信息:“fastcgi_pass 127.0.0.1:9000;”,执行命令“lsof -i:9000”可以看到9000端口刚好是PHP-FPM进程。
Nginx 将请求信息传给了 PHP-FPM,PHP-FPM 分配一个 Worker 进程处理,Worker 进程注册变量 $_GET/$_POST
等,根据请求信息访问指定的 PHP 脚本文件,然后使用 PHP 解释器执行。
(我的理解是:相当于 PHP-FPM 启动了 PHP 解释器,有点像执行了 php -f script.php
命令)
网络请求的信息层层传递,最终到达 PHP,所以在 PHP 代码里可以获取到本次 HTTP 请求的各种参数。
执行哪个 PHP 脚本由 Nginx 告诉 PHP-FPM,在 Nginx 配置中可见一行:
fastcgi_param SCRIPT_FILENAME /home/dev_user/www/xxx/webroot/index.php;
若不指定 PHP 文件,就会使用该默认配置,尝试使用根目录下的 index.php 文件。index.php 里会启动框架程序,由框架找到对应的 Controller 和 Action,完成实际业务逻辑。
Atas ialah kandungan terperinci PHP内存溢出、命令行和Web服务两种执行方式的理解. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!