PHP 在 background 處理資料的另一種方法

一般用 PHP 實作 background task,大多都是先將 task 處理後再處理 user request。
所以程式大概是長這樣:

<?php

class Contrller
{
    public function handle()
    {
        if (Background::hasTask()) {
            Background::run();
        }

        $data = List::whereIn('id', [1, 4, 6])->get();
        view($data);
}

這樣做會有一些缺點,若背景程式跑了很久才結束,會讓使用者有一種「點了按鍵卻感覺沒有回應」的錯覺。

若有時候真的累積不少 background task 需要處理時,為了不讓使用者等太久,通常會 trigger 一些專為 background task 設定的程式去執行,像是一群的 consumer,或者會 trigger gearman 去處理。

而我再某一次很罕見的狀況下必須馬上讓使用者先收到 reponse 之後才去處理 background task。問題來了:通常都要 PHP return / exit 以後,response 才會傳回 client,那有什麼辦法先給已經處理好的 response,讓 client 繼續瀏覽以後再來跑 background task?

沒想到還真的讓我找到解法:fastcgi_finish_request()

fastcgi_finish_request()這個函式會先通知 fastcgi 的上層 (我這邊是 Nginx reverse proxy) 要給 client 的資料都送出去囉,可以 ending 啦。然後 PHP 偷偷摸摸繼續在後面弄東西。
程式上寫起來會有點不一樣:

<?php

class Contrller
{
    public function handle()
    {
        $data = List::whereIn('id', [1, 4, 6])->get();
        view($data);
        fastcgi_finish_request();  // 這之後不管發生什麼事,client 都不會再收到訊息

        Background::run()
}

不過要注意,因為不管如何,client 都不再收到訊息,因此若有錯誤發生是很難 debug 的。
catch 也好,用 logger 也好,甚至你要請到 register_shutdown_function() 出場也都好,如果不這樣做,基本上沒什麼 debug 手段。

而我使用 fastcgi_finish_request() 也只有在 Nginx + php-fpm 時成功,我不曉得會做其他形式的 web server 架構是否可以使用。如果網友嘗試過,不如回覆讓其他人知道一下。

總之不得已才使用這個方法

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

Exit mobile version