php利用fastcgi_finish_request實現非阻塞及相關拓展

前言

在實際項目中常常會有這樣的需求,對於前端發過來的請求,須要在後端進行長時間的處理,但爲了讓使用者有更好的體驗,爲了讓PHP在後端處理長時間任務時不阻塞,快速響應頁面請求,所以在這裏對fastcgi_finish_request的應用進行總結概括。固然php實現非阻塞的方式有不少種,好比異步腳本、swoole,但我的認爲fastcgi_finish_request最爲簡單方便。php

基本應用

fastcgi_finish_request介紹

(PHP 5 >= 5.3.3, PHP 7)html

fastcgi_finish_request — 沖刷(flush)全部響應的數據給客戶端前端

boolean fastcgi_finish_request ( void )

此函數沖刷(flush)全部響應的數據給客戶端並結束請求。 這使得客戶端結束鏈接後,須要大量時間運行的任務可以繼續運行。後端

返回值

成功時返回 TRUE, 或者在失敗時返回 FALSE服務器

注意問題

  • PHP 與 Web 服務器使用了PHP-FPM(FastCGI進程管理器),那經過fastcgi_finish_request() 函數能立刻結束會話,而 PHP 線程能夠繼續在後臺運行。也就是說只針對php-fpm的進程管理方式才能使用該函數
  • 只要代碼運行到這個位置,就已經斷開請求返回參數給客戶端了。接下來的代碼都和客戶端沒有關係了。也就是說對於輸出在頁面的內容必須放在fastcgi_finish_request函數以前
  • fastcgi_finish_request()結束客戶端鏈接以後,運行時間依然會受max_execution_time超時時間的影響,也就是說若是預計到代碼在後端執行時間比較久,仍是要設定set_time_limit(0)
  • 在高併發下執行時間太久也會致使fastcgi進程不夠用,不能及時釋放,就會爆502錯誤了。

應用

echo "program start...";

file_put_contents('/tmp/garylog.log','start-time:'.date('Y-m-d H:i:s')."\n", FILE_APPEND);

fastcgi_finish_request();sleep(1);

// set_time_limit(0);
// sleep(150);

$num = 25;
$num += 1;
sleep(5);
echo 'debug...';
file_put_contents('/tmp/garylog.log', 'start-proceed:'.$num.',時間'.date('Y-m-d H:i:s')."\n", FILE_APPEND);

sleep(10);

file_put_contents('/tmp/garylog.log', 'end-time:'.date('Y-m-d H:i:s')."\n", FILE_APPEND);

運行測試
圖片描述swoole

圖片描述

兼容非php-fpm

從代碼的可移植性講的話, 能夠在代碼中附上以下代碼:併發

if (!function_exists("fastcgi_finish_request")) {
          function fastcgi_finish_request()  {
          }
    }

不會形成代碼部署在非fpm環境下形成問題.異步

保證進程單一運行

對於上面說到的問題:在高併發下執行時間太久也會致使fastcgi進程不夠用,不能及時釋放。同時咱們的需求僅僅是爲了起到觸發的做用,並不須要每次運行,那麼能夠考慮使用下面的方法,避免重複佔用進程。函數

$processId = realpath(__FILE__) . '-' . get_class($this);
$filename = md5($processId);
$file = '/tmp/'.$filename;
if(!file_exists($filename)){
    file_put_contents($file, getmypid());
}else{
    return true; 
}


## do somthing 須要長時間處理的代碼

//處理完成後刪除進程id記錄文件
unlink($file);

參考

相關文章
相關標籤/搜索