Swoole 程式設計須知

coderChenZR 發表於 2022-06-23
Swoole

這個頻道內會詳細介紹非同步程式設計與同步程式設計的不同之處以及需要注意的事項。

注意事項

  • 不要在程式碼中執行 sleep以及其他睡眠函式,這樣會導致整個程式阻塞
  • exit/die是危險的,會導致worker程式退出
  • 可通過 register_shutdown_function來捕獲致命錯誤,在程式異常退出時做一些請求工作
  • PHP程式碼中如果有異常丟擲,必須在回撥函式中進行 try/catch捕獲異常,否則會導致工作程式退出
  • swoole不支援 set_exception_handler,必須使用 try/catch方式處理異常
  • Worker程式不得共用同一個 Redis MySQL等網路服務客戶端,Redis/MySQL建立連線的相關程式碼可以放到 onWorkerStart回撥函式中。

類/函式重複定義

新手非常容易犯這個錯誤,由於swoole是常駐記憶體的,所以載入類/函式定義的檔案後不會釋放。因此引入類/函式的php檔案時必須要使用 include_once require_once,否會發生 cannot redeclare function/class 的致命錯誤。

記憶體管理

PHP守護程式與普通Web程式的變數生命週期、記憶體管理方式完全不同。請參考  swoole_server記憶體管理 頁面。 編寫 swoole_server或其他常駐程式時需要特別注意。

程式隔離

程式隔離也是很多新手經常遇到的問題。修改了全域性變數的值,為什麼不生效,原因就是全域性變數在不同的程式,記憶體空間是隔離的,所以無效。所以使用swoole開發Server程式需要了解 程式隔離問題。

  • 不同的程式中PHP變數不是共享,即使是全域性變數,在A程式內修改了它的值,在B程式內是無效的
  • 如果需要在不同的Worker程式內共享資料,可以用 Redis MySQL 檔案 Swoole\Table APCu shmget等工具實現
  • 不同程式的檔案控制程式碼是隔離的,所以在A程式建立的Socket連線或開啟的檔案,在B程式內是無效,即使是將它的fd傳送到B程式也是不可用的

sleep/usleep的影響

在非同步IO的程式中, 不得使用sleep/usleep/time_sleep_until/time_nanosleep。(下文中使用sleep泛指所有睡眠函式)

  • sleep函式會使程式陷入睡眠阻塞
  • 直到指定的時間後作業系統才會重新喚醒當前的程式
  • sleep過程中,只有訊號可以打斷
  • 由於swoole的訊號處理是基於signalfd實現的,所以即使傳送訊號也無法中斷sleep

swoole提供的 swoole_event_add swoole_timer_tick swoole_timer_after swoole_process::signal 非同步swoole_client 在程式sleep後會停止工作。 swoole_server也無法再處理新的請求。

例項程式

$serv = new swoole_server("127.0.0.1", 9501);$serv->on('receive', function ($serv, $fd, $from_id, $data) {    sleep(100);    $serv->send($fd, 'Swoole: '.$data);
});$serv->start();

onReceive事件中執行了sleep函式,server在100秒內無法再收到任何客戶端請求。

exit/die函式的影響

在swoole程式中禁止使用exit/die,如果PHP程式碼中有 exit/die,當前工作的Worker程式、Task程式、User程式、以及swoole_process程式會立即退出。

建議使用try/catch的方式替換exit/die,實現中斷執行跳出PHP函式呼叫棧。

function swoole_exit($msg)
{    //php-fpm的環境
    if (ENV=='php')
    {        exit($msg);
    }    //swoole的環境
    else
    {        throw new Swoole\ExitException($msg);
    }
}

異常處理的方式比 exit/die更友好,因為異常是可控的, exit/die不可控。在最外層進行try/catch即可捕獲異常,僅終止當前的任務。Worker程式可以繼續處理新的請求,而 exit/die會導致程式直接退出,當前程式儲存的所有變數和資源都會被銷燬。如果程式內還有其他任務要處理,遇到 exit/die也將全部丟棄。

while迴圈的影響

非同步程式如果遇到死迴圈,事件將無法觸發。非同步IO程式使用 Reactor模型,執行過程中必須在 reactor->wait處輪詢。如果遇到死迴圈,那麼程式的控制權就在while中了, reactor無法得到控制權,無法檢測事件,所以IO事件回撥函式也將無法觸發。

密集運算的程式碼不是阻塞

例項程式

$serv = new swoole_server("127.0.0.1", 9501);$serv->on('receive', function ($serv, $fd, $from_id, $data) {    while(1)
    {        $i ++;
    }    $serv->send($fd, 'Swoole: '.$data);
});$serv->start();

onReceive事件中執行了死迴圈,server在無法再收到任何客戶端請求,必須等待迴圈結束才能繼續處理新的事件。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70018476/viewspace-2902541/,如需轉載,請註明出處,否則將追究法律責任。