原生PHP代碼實現耗時任務后台異步偽並發執行


目前已在公司項目中完美使用,應用場景僅適合NGINX+PHP-FPM。APACHE FCGI模式未測試。偽並發執行指NGINX給FPM子進程分配任務時,可以多個進程實現同時工作,並非處理高並發請求。

// 設置客戶端斷開連接時不中斷腳本的執行
ignore_user_abort(true);
 
// 以下代碼開始告訴NGINX響應已經成功得到響應內容可以關閉請求了。
# 擦除緩沖區的內容並關閉,然后在啟動新的ob緩沖
ob_end_clean(); ob_start();
# 輸出響應數據,這里模擬輸出json。320:中文不編碼+不轉移斜杠[JSON_UNESCAPED_UNICODE + JSON_UNESCAPED_SLASHES] = 320
echo json_encode(['status' => true, 'message' => '任務開始執行', 'date' => null], 320);
$size = ob_get_length();
# 響應內容長度
header("Content-Length: $size");
# 告訴NGINX可以關閉http連接了
header("Connection: close");
# 此次請求已收到並正常處理
header("HTTP/1.1 200 OK");
 
# 刷新輸出緩沖區內容並關閉ob緩沖
ob_end_flush(); 
# 如果緩沖區還有內容,再次刷新輸出ob緩沖塊的內容
ob_get_length() && ob_flush();
# 再次刷新輸出,沖刷Web Server的緩沖區。例如用於防范NGINX GZIP或 APACHE mod_gzip自己的緩沖區
flush();
# 沖刷所有響應的數據給客戶端。
function_exists("fastcgi_finish_request") && fastcgi_finish_request();
session_write_close(); # 關閉session寫入,取消對session進行IO的鎖
 
// 設置最大執行時間,這里為15分鍾
ini_set('max_execution_time', 15 * 60);
set_time_limit(15 * 60);
// 額外設置
error_reporting(0); # 屏蔽所有報告異常
ini_set('memory_limit', '512M'); # 臨時設置內存,若默認運行內存128M夠用,無需再次設置
date_default_timezone_set('Asia/Shanghai'); # 時區
 
// code ...
// error_log('任務執行完成'); UPDATE TABLE ...
 
exit;

為何要沖刷那么多次緩沖區?

PHP的數據寫入順序: ob_start(),將內部緩沖區(buffer)打開。當PHP遇到echo,printf等輸出語句時,PHP就會將要輸出的數據放入緩沖區(buffer)中,等待輸出。而只有當緩沖區滿了或者PHP運行完畢,才將數據輸出去。輸出字節離開PHP緩沖區進去Apache緩沖區或者Nginx緩沖區(fast-cgi),之后進入瀏覽器緩沖區。

注意:此代碼不適合高並發請求大耗時任務巨長的工作

因FPM子進程工作滿載中,NGINX無法分配任務,以至於任務不能及時處理,造成NGINX報出502錯誤,導致任務處理失敗。這種場景可選擇開啟更多的NGINX WORKER進程和更多的FPM子進程,或使用其它解決方案。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM