目前已在公司項目中完美使用,應用場景僅適合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子進程,或使用其它解決方案。
