Swoole 編程須知

2022-07-12 11:22 更新

編程須知

這個頻道內(nèi)會詳細(xì)介紹異步編程與同步編程的不同之處以及需要注意的事項。

注意事項

  • 不要在代碼中執(zhí)行sleep以及其他睡眠函數(shù),這樣會導(dǎo)致整個進(jìn)程阻塞
  • exit/die是危險的,會導(dǎo)致worker進(jìn)程退出
  • 可通過register_shutdown_function來捕獲致命錯誤,在進(jìn)程異常退出時做一些請求工作
  • PHP代碼中如果有異常拋出,必須在回調(diào)函數(shù)中進(jìn)行try/catch捕獲異常,否則會導(dǎo)致工作進(jìn)程退出
  • swoole不支持set_exception_handler,必須使用try/catch方式處理異常
  • Worker進(jìn)程不得共用同一個RedisMySQL等網(wǎng)絡(luò)服務(wù)客戶端,Redis/MySQL創(chuàng)建連接的相關(guān)代碼可以放到onWorkerStart回調(diào)函數(shù)中。

類/函數(shù)重復(fù)定義

新手非常容易犯這個錯誤,由于swoole是常駐內(nèi)存的,所以加載類/函數(shù)定義的文件后不會釋放。因此引入類/函數(shù)的php文件時必須要使用include_oncerequire_once,否會發(fā)生cannot redeclare function/class 的致命錯誤。

內(nèi)存管理

PHP守護(hù)進(jìn)程與普通Web程序的變量生命周期、內(nèi)存管理方式完全不同。請參考 swoole_server內(nèi)存管理 頁面。編寫swoole_server或其他常駐進(jìn)程時需要特別注意。

進(jìn)程隔離

進(jìn)程隔離也是很多新手經(jīng)常遇到的問題。修改了全局變量的值,為什么不生效,原因就是全局變量在不同的進(jìn)程,內(nèi)存空間是隔離的,所以無效。所以使用swoole開發(fā)Server程序需要了解進(jìn)程隔離問題。

  • 不同的進(jìn)程中PHP變量不是共享,即使是全局變量,在A進(jìn)程內(nèi)修改了它的值,在B進(jìn)程內(nèi)是無效的
  • 如果需要在不同的Worker進(jìn)程內(nèi)共享數(shù)據(jù),可以用Redis、MySQL、文件、Swoole\Table、APCu、shmget等工具實現(xiàn)
  • 不同進(jìn)程的文件句柄是隔離的,所以在A進(jìn)程創(chuàng)建的Socket連接或打開的文件,在B進(jìn)程內(nèi)是無效,即使是將它的fd發(fā)送到B進(jìn)程也是不可用的

sleep/usleep的影響

在異步IO的程序中,不得使用sleep/usleep/time_sleep_until/time_nanosleep。(下文中使用sleep泛指所有睡眠函數(shù))

  • sleep函數(shù)會使進(jìn)程陷入睡眠阻塞
  • 直到指定的時間后操作系統(tǒng)才會重新喚醒當(dāng)前的進(jìn)程
  • sleep過程中,只有信號可以打斷
  • 由于swoole的信號處理是基于signalfd實現(xiàn)的,所以即使發(fā)送信號也無法中斷sleep

swoole提供的swoole_event_add、swoole_timer_tick、swoole_timer_after、swoole_process::signal異步swoole_client 在進(jìn)程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事件中執(zhí)行了sleep函數(shù),server在100秒內(nèi)無法再收到任何客戶端請求。


exit/die函數(shù)的影響

在swoole程序中禁止使用exit/die,如果PHP代碼中有exit/die,當(dāng)前工作的Worker進(jìn)程、Task進(jìn)程、User進(jìn)程、以及swoole_process進(jìn)程會立即退出。

建議使用try/catch的方式替換exit/die,實現(xiàn)中斷執(zhí)行跳出PHP函數(shù)調(diào)用棧。

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

異常處理的方式比exit/die更友好,因為異常是可控的,exit/die不可控。在最外層進(jìn)行try/catch即可捕獲異常,僅終止當(dāng)前的任務(wù)。Worker進(jìn)程可以繼續(xù)處理新的請求,而exit/die會導(dǎo)致進(jìn)程直接退出,當(dāng)前進(jìn)程保存的所有變量和資源都會被銷毀。如果進(jìn)程內(nèi)還有其他任務(wù)要處理,遇到exit/die也將全部丟棄。


while循環(huán)的影響

異步程序如果遇到死循環(huán),事件將無法觸發(fā)。異步IO程序使用Reactor模型,運行過程中必須在reactor->wait處輪詢。如果遇到死循環(huán),那么程序的控制權(quán)就在while中了,reactor無法得到控制權(quán),無法檢測事件,所以IO事件回調(diào)函數(shù)也將無法觸發(fā)。

密集運算的代碼不是阻塞

實例程序

$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事件中執(zhí)行了死循環(huán),server在無法再收到任何客戶端請求,必須等待循環(huán)結(jié)束才能繼續(xù)處理新的事件。



以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號