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