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