php提前響應請求繼續執行程式碼(偽非同步)
- 2019 年 12 月 19 日
- 筆記
在很多業務需求中,我們都可能需要先讓php給瀏覽器輸出,然後在後台慢慢處理其他不用輸出耗時的業務.
那麼,php該怎麼實現這個功能呢?
ignore_user_abort(true);
首先,我們先來了解下ignore_user_abort(true);這個函數
這個函數可以忽略客戶機的斷開,繼續執行php程式碼
那到底這個用來幹啥的呢?例如:
//當用戶A用瀏覽器請求下單邏輯 //由於後台邏輯非常多,需要處理20秒 //用戶A等了10秒等不下去,關閉了網頁 //默認情況下,用戶關閉了網頁,php進程則會直接終止,相當於執行了一半邏輯之後,停止了 //用戶後面發現,自己已經有了這個訂單數據,卻沒有訂單詳情(執行一半沒來得及插入)
這個時候,ignore_user_abort就有用了,當忽略客戶機斷開後,php會一直執行,直到異常終止或已完成操作
set_time_limit(0);
在上面講到,如果啟用ignore_user_abort 則會讓php一直執行,直到異常終止,而在php常規web模式下,默認有個執行超時時間(30秒),當執行到30秒時,會直接終止該php進程,可使用set_time_limit(0),設置為用不超時,這樣的話,客戶端就算斷開,就算超過30秒,php進程也會一直執行下去,直到執行完成
實時輸出
在我之前的一篇講buffer緩衝區的文章中,有講到過瀏覽器實時輸出,刷新緩衝區可以讓php+web伺服器的輸出變成實時輸出,不再需要等待腳本結束才顯示內容.然而,apache和nginx的實現方式也有所不同
<?php //apache方法,需要關閉apache緩衝區 for($i=0;$i<1000;$i++){ echo $i; ob_flush();//刷新PHP自身緩衝區 flush();//刷新(特指apache)web伺服器的緩衝區,輸出數據 sleep(1); } //nginx緩衝區 ob_end_clean(); ob_implicit_flush(); header('X-Accel-Buffering: no'); // 關鍵是加了這一行。 for($i=0;$i<1000;$i++){ echo $i; sleep(1); }
用以上方法,就可以使php的echo,實時輸出到瀏覽器中
偽結束響應
在認識到上面3種概念之後,我們就要開始實現這個功能了 偽結束響應原理是:
先讓php提前輸出"已結束響應"程式碼(其實還沒有結束,還可以繼續echo輸出)
然後讓用戶自行關閉窗口,通過set_time_limit和ignore_user_abort函數實現php程式碼還在後台運行,如以下例子:
<?php //apache伺服器 set_time_limit(0); ignore_user_abort(true); //巴拉巴拉這裡處理了一些事情 echo "完成請求,3秒自動關閉頁面(一段js自動關閉頁面)"; ob_flush();//刷新PHP自身緩衝區 flush();//刷新(特指apache)web伺服器的緩衝區,輸出數據 //這裡還在巴拉巴拉處理事情 $i=0; while(1){ //注意,死循環非常危險,會造成該web進程一直在處理,不會退出,永久佔用一個進程,而且管理該進程非常麻煩,建議加個判斷啥的 file_put_contents('test.txt',$i); $i++; sleep(1); } //nginx伺服器 set_time_limit(0); ignore_user_abort(true); //巴拉巴拉這裡處理了一些事情 ob_end_clean(); ob_implicit_flush(); header('X-Accel-Buffering: no'); // 關鍵是加了這一行。 echo "完成請求,3秒自動關閉頁面(一段js自動關閉頁面)"; //這裡還在巴拉巴拉處理事情 $i=0; while($i<100){ //注意,死循環非常危險,會造成該web進程一直在處理,不會退出,永久佔用一個進程,而且管理該進程非常麻煩,建議加個判斷啥的 file_put_contents('test.txt',$i); $i++; sleep(1); }
提前結束響應
在php-fpm中,有個函數fastcgi_finish_request可使得web伺服器提前中斷http響應:
<?php //php-fpm模式下 set_time_limit(0); ignore_user_abort(true); //巴拉巴拉這裡處理了一些事情 echo "完成請求,3秒自動關閉頁面(一段js自動關閉頁面)"; fastcgi_finish_request();//真正的結束響應,後面的echo將不起作用 //這裡還在巴拉巴拉處理事情 $i=0; while($i<100){ //注意,死循環非常危險,會造成該web進程一直在處理,不會退出,永久佔用一個進程,而且管理該進程非常麻煩,建議加個判斷啥的 file_put_contents('test.txt',$i); $i++; sleep(1); }
在非fpm模式下,該怎麼提前中斷呢?
<?php //非php-fpm 一般是apache set_time_limit(0); ignore_user_abort(true); ob_end_flush(); ob_start(); //巴拉巴拉這裡處理了一些事情 echo '完成請求,3秒自動關閉頁面(一段js自動關閉頁面)'; header("Content-Type: text/html;charset=utf-8"); header("Connection: close");//告訴瀏覽器不需要保持長連接 header('Content-Length: '. ob_get_length());//告訴瀏覽器本次響應的數據大小只有上面的echo那麼多 ob_flush(); flush(); //header不會經過buffer,直接輸出到瀏覽器,瀏覽器接收到之後,直接主動關閉連接 //這裡還在巴拉巴拉處理事情 $i=0; while($i<100){ //注意,死循環非常危險,會造成該web進程一直在處理,不會退出,永久佔用一個進程,而且管理該進程非常麻煩,建議加個判斷啥的 file_put_contents('test.txt',$i); $i++; sleep(1); }
本文為仙士可原創文章,轉載無需和我聯繫,但請註明來自仙士可部落格www.php20.cn
- 上一篇: 關於"絕對路徑"和"相對路徑"
- 下一篇: swoole/easyswoole 新手入門教程