這個頻道內(nèi)會詳細(xì)介紹異步編程與同步編程的不同之處以及需要注意的事項。
新手非常容易犯這個錯誤,由于swoole是常駐內(nèi)存的,所以加載類/函數(shù)定義的文件后不會釋放。因此引入類/函數(shù)的php文件時必須要使用include_once或require_once,否會發(fā)生cannot redeclare function/class 的致命錯誤。
PHP守護(hù)進(jìn)程與普通Web程序的變量生命周期、內(nèi)存管理方式完全不同。請參考 swoole_server內(nèi)存管理 頁面。編寫swoole_server或其他常駐進(jìn)程時需要特別注意。
進(jìn)程隔離也是很多新手經(jīng)常遇到的問題。修改了全局變量的值,為什么不生效,原因就是全局變量在不同的進(jìn)程,內(nèi)存空間是隔離的,所以無效。所以使用swoole開發(fā)Server程序需要了解進(jìn)程隔離問題。
在異步IO的程序中,不得使用sleep/usleep/time_sleep_until/time_nanosleep。(下文中使用sleep泛指所有睡眠函數(shù))
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)無法再收到任何客戶端請求。
在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也將全部丟棄。
異步程序如果遇到死循環(huán),事件將無法觸發(fā)。異步IO程序使用Reactor模型,運(yùn)行過程中必須在reactor->wait處輪詢。如果遇到死循環(huán),那么程序的控制權(quán)就在while中了,reactor無法得到控制權(quán),無法檢測事件,所以IO事件回調(diào)函數(shù)也將無法觸發(fā)。
密集運(yùn)算的代碼不是阻塞
$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ù)處理新的事件。
更多建議: