说到PHP执行时间,相信每个PHPer都遇到过这个问题,尤其是CGI模式下。一般我们会修改max_execution_time或者增加set_time_limit(0)来解决问题,但是你可能遇到过以下场景:我们先设置php.ini的执行时间。程序模拟运行20S:set_time_limit(60);sleep(20);echo1;你会发现程序在执行到16S的时候报502BadGateway,那么可以执行60S?按照江湖规则,先看日志,查php-fpm.log,可以找到这么一条信息[06-Dec-201912:44:13]WARNING:[poolwww]child19910,脚本'/home/wwwroot/public/index.php'(请求:“GET/index.php”)执行超时(16.120721秒),终止[2019年12月6日12:44:13]警告:[poolwww]child19910exitedonsignal15(SIGTERM)after2573.443300secondsfromstart[06-Dec-201912:44:13]NOTICE:[poolwww]child21861started这三行日志分别告诉我们三条信息1.子进程19910执行时间超过16STerminated2.子进程19910在2573.44S后被关闭。3、子进程child21861在子进程19910关闭后的同一秒fork出来并开始运行。也就是说,在PHP-CGI的执行过程中,除了我们之前设置的两个参数外,还应该有一个参数来限制这个过程的执行时间。打开php目录下的php-fpm.conf看看有没有异常...pm.min_spare_servers=16pm.max_spare_servers=60request_terminate_timeout=15request_slowlog_timeout=0slowlog=var/log/slow.log...可以看到有一个参数request_terminate_timeout=15非常接近我们的超时阈值。翻翻注释找到这个参数的解释:;工作进程将服务于单个请求的超时时间;被杀。这个选项应该在'max_execution_time'ini选项时使用;由于某种原因不会停止脚本执行。值“0”表示“关闭”。大概意思就是这个参数max_execution_time的设置是一种保护措施,防止php子进程因为某些原因停止。当然这种保护措施比较简单粗暴,就是直接把超时的子进程kill掉,然后直接fork一个新的组合Explain,我们可以很容易理解前面日志中出现的三个信息:因为执行时间超过了max_execution_time设置的阈值,子进程19910被直接杀死,然后又产生了新的子进程21861,所以我们将php-fpm.conf中的request_terminate_timeout改为30,重启php,再次执行之前的代码,并且不再报502,看到这里可能有朋友会说PHP的执行时间影响了很多参数,实在想不起来应该改一下才真正有效。我们不妨梳理一下php运行的架构。不了解php运行架构的朋友可以看看我之前写的《浅析PHP-FPM、CGI、Fast CGI的关系》PHP-FPM的程序架构。管理一个PHP-CGI子进程池,当master进程有请求转发给worker时,master进程会开始计时,当超过设定的执行时间后,master进程会直接kill掉已经发生的worker进程timedout(程序的世界不易混),而我们设置的max_execution_time时间是针对worker进程的,所以单个worker进程的执行时间无论设置多少,都不能超过在fpm中传递request_terminate_timeout,否则killallSeconds,当脚本运行25秒时调用set_time_limit(20),那么脚本总共可以运行45秒才超时。而配置指令max_execution_time只影响脚本本身的执行时间。不包括使用system()的系统调用、流操作、数据库操作等发生的脚本执行的最大时间。也就是说,sleep或file_get_contents等操作消耗的时间不会计入max_execution_time的超时时间。所以其实我上面写的代码即使设置sleep为999也不会报执行超时错误,用代码验证一下:set_time_limit(10);睡觉(20);可以发现程序并没有报超时错误,接下来我们写一段代码让PHP执行非系统调用、数据流操作等耗时任务。设置时间限制(10);//将计数器清零,允许执行时间为10Sleep(10);$json[]=str_repeat("123456789,",10000);//生成一个内容很大的数组$count=1000000;//循环执行数据1000000次json_encode和json_decode转换长字符串效率不高,所以比较耗时while($count--){$string=json_encode($json);json_decode($string,true);}结果如上图所示,程序一共执行了20秒,其中10秒处于休眠状态,即系统调用不计入执行超时period,另外10秒执行的是cpu密集型操作,符合计入max_execution_time超时时间的要求,所以满足条件就抛出错误。文末总结:有时间的话可以多翻翻说明书,每天都会有新的发现
