php里面的耗时操作,PHP执行时间那点事
說起php的執(zhí)行時(shí)間,相信每一個(gè)phper都遇到過這方面的問題,特別是在CGI模式下,一般我們都會(huì)通過修改max_execution_time或者在代碼開頭添加set_time_limit(0)來解決問題,但下面這個(gè)場(chǎng)景大家可能也曾經(jīng)遇到過:
我們先將php.ini的執(zhí)行時(shí)間設(shè)置為60S
max_execution_time = 60
再在代碼的開頭設(shè)置執(zhí)行時(shí)間為60S,讓兩者統(tǒng)一
然后運(yùn)行sleep讓程序模擬運(yùn)行20S:
set_time_limit(60);
sleep(20);
echo 1;
會(huì)發(fā)現(xiàn)程序在執(zhí)行到第16S的時(shí)候就報(bào)出了502 Bad Gateway
說好的可以執(zhí)行60S呢?江湖規(guī)矩報(bào)錯(cuò)先翻看日志,查看php-fpm.log,可以發(fā)現(xiàn)有這么一段信息
[06-Dec-2019 12:44:13] WARNING: [pool www] child 19910, script '/home/wwwroot/public/index.php' (request: "GET /index.php") execution timed out (16.120721 sec), terminating
[06-Dec-2019 12:44:13] WARNING: [pool www] child 19910 exited on signal 15 (SIGTERM) after 2573.443300 seconds from start
[06-Dec-2019 12:44:13] NOTICE: [pool www] child 21861 started
這三行日志分別告訴了我們?nèi)齻€(gè)信息
1.子進(jìn)程19910的執(zhí)行時(shí)間超過了16S被終結(jié)了
2.子進(jìn)程19910在啟動(dòng)了2573.44S后被關(guān)閉了
3.子進(jìn)程19910在關(guān)閉后的同一秒子進(jìn)程child 21861被fork出來開始運(yùn)行
也就是說,PHP-CGI在執(zhí)行的過程中,除去我們之前已經(jīng)設(shè)置好的兩個(gè)參數(shù),應(yīng)該還有另外一個(gè)參數(shù)限制了這個(gè)進(jìn)程的執(zhí)行時(shí)間,打開php目錄下的php-fpm.conf看看有無異常
...
pm.min_spare_servers = 16
pm.max_spare_servers = 60
request_terminate_timeout = 15
request_slowlog_timeout = 0
slowlog = var/log/slow.log
...
可以看到有一個(gè)參數(shù)request_terminate_timeout = 15 和我們的超時(shí)閾值非常接近,翻看注釋找到關(guān)于這個(gè)參數(shù)的解釋:
; The timeout for serving a single request after which the worker process will
; be killed. This option should be used when the 'max_execution_time' ini option
; does not stop script execution for some reason. A value of '0' means 'off'.
大概的意思是這個(gè)參數(shù)的設(shè)置是當(dāng)max_execution_time啟用時(shí),為了防止php子進(jìn)程因?yàn)槟承┰驘o法停止運(yùn)行而設(shè)置的一個(gè)保護(hù)措施,當(dāng)然這個(gè)保護(hù)措施比較簡(jiǎn)單粗暴,就是直接kill超時(shí)的子進(jìn)程,然后直接fork一個(gè)新的
結(jié)合解釋,我們就很好理解前面日志中出現(xiàn)的三條信息了:因?yàn)閳?zhí)行時(shí)間超過了max_execution_time設(shè)置的閾值,子進(jìn)程19910被直接kill了,然后又生成了一個(gè)新的子進(jìn)程21861
于是我們將php-fpm.conf中的request_terminate_timeout改為30,重啟php,再次執(zhí)行之前的代碼,不再報(bào)502了
看到這里,可能會(huì)有小伙伴會(huì)說,PHP的執(zhí)行執(zhí)行時(shí)間影響的參數(shù)有點(diǎn)多,真的記不住應(yīng)該改那個(gè)才真正的有效,我們不妨從php運(yùn)行的架構(gòu)來梳理一下,不太清楚php運(yùn)行架構(gòu)的小伙伴可以看看我之前寫的《淺析PHP-FPM、CGI、Fast CGI的關(guān)系》
PHP-FPM的程序架構(gòu)是由一個(gè)master進(jìn)程來進(jìn)行管理一個(gè)PHP-CGI的子進(jìn)程池,當(dāng)一個(gè)請(qǐng)求由master進(jìn)程轉(zhuǎn)發(fā)到worker時(shí),master進(jìn)程便會(huì)開始計(jì)時(shí),當(dāng)超過設(shè)定的執(zhí)行時(shí)間時(shí)master進(jìn)程,便會(huì)直接kill掉超時(shí)的worker進(jìn)程(程序的世界也不好混),而我們?cè)O(shè)置max_execution_time設(shè)置的時(shí)間是對(duì)于workder進(jìn)程而言的,所以無論單個(gè)worker進(jìn)程的執(zhí)行時(shí)間設(shè)置多少,都不得超過fpm中的request_terminate_timeout,否則一律kill
文末,再補(bǔ)充兩條在翻看手冊(cè)時(shí)翻到的知識(shí)點(diǎn):
在代碼中使用set_time_limit()會(huì)從零開始重新啟動(dòng)超時(shí)計(jì)數(shù)器
換句話說,如果超時(shí)默認(rèn)是30秒,在腳本運(yùn)行了了25秒時(shí)調(diào)用 set_time_limit(20),那么,腳本在超時(shí)之前可運(yùn)行總時(shí)間為45秒。
這個(gè)相對(duì)簡(jiǎn)單,就不上測(cè)試代碼了,大家有空可以驗(yàn)證一下
set_time_limit()函數(shù)和配置指令max_execution_time只影響腳本本身執(zhí)行的時(shí)間
其他發(fā)生在諸如使用system()的系統(tǒng)調(diào)用,流操作,數(shù)據(jù)庫操作等的腳本執(zhí)行的最大時(shí)間不包括其中。也就是說,比如sleep或者file_get_contents等操作消耗的時(shí)間是不會(huì)計(jì)入max_execution_time的超時(shí)時(shí)間中的
所以其實(shí)我前文寫的代碼即使把sleep設(shè)置為999也不會(huì)報(bào)執(zhí)行超時(shí)的錯(cuò)誤
,用代碼驗(yàn)證下:
set_time_limit(10);
sleep(20);
可以發(fā)現(xiàn)程序確實(shí)沒有報(bào)超時(shí)的錯(cuò)誤,接著我們?cè)倬帉懸欢未a,讓php執(zhí)行非系統(tǒng)調(diào)用和數(shù)據(jù)流等操作的耗時(shí)任務(wù)
set_time_limit(10); //將計(jì)數(shù)器清零,允許執(zhí)行時(shí)間為10S
sleep(10);
$json[] = str_repeat("123456789,",10000); //生成一個(gè)內(nèi)容量較大的數(shù)組
$count = 1000000;
//循環(huán)執(zhí)行1000000次數(shù)據(jù),json_encode和json_decode在對(duì)于長字符串的轉(zhuǎn)化效率不高,所以比較耗時(shí)
while ($count--){
$string = json_encode($json);
json_decode($string,true);
}
結(jié)果如上圖,程序一共執(zhí)行了20S,其中有10S是在sleep也就是系統(tǒng)調(diào)用不算入執(zhí)行超時(shí)時(shí)間,另外10s執(zhí)行的是一段cpu密集型的操作,符合算入max_execution_time超時(shí)時(shí)間的要求,于是符合條件拋出錯(cuò)誤
文末總結(jié):有空可以多翻翻手冊(cè),每天都有新發(fā)現(xiàn)
總結(jié)
以上是生活随笔為你收集整理的php里面的耗时操作,PHP执行时间那点事的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: base64的php文件上传,PHP传统
- 下一篇: oracle avg分析函数,分析函数之