下面給出了思考過程及解決問題的demo程式碼。
首先分析了一下,Swoole 是多進程模式執行的,分為 Master
、Manager
、Worker
進程。
Master 進程就是我們啟動服務的 cli 命令檔案所在進程,在這裡面初始化有一個問題,這裡所有載入的類、全域性變數,其它 Worker 進程裡都可以使用,無法熱重新啟動生效。
Manager 進程的情況基本和上面差不多。
那麼只有在 Worker 進程做處理了,但如果寫在 WorkerStart 事件裡,每個 Worker 進程都會去執行。
WorkerStart
事件定義:
function onWorkerStart(swoole_server $server, int $worker_id);
$worker_id是一個從0-$worker_num之間的數位,表示這個Worker進程的ID
那這個就好辦了,直接判斷workerid為0的去觸發專案初始化事件。剩下還有一個問題就是,如何在初始化執行完成前,讓所有 Worker 進程暫時都不處理請求。
思考並嘗試了一下,這個問題可以通過協程掛起來解決,demo 程式碼如下:
<?php use SwooleCoroutine; $http = new swoole_http_server('127.0.0.1', 8080); $http->on('WorkerStart', function(swoole_http_server $server, $workerId){ $initFlagFile = __DIR__ . '/init.flag'; if(0 === $server->worker_id && (!is_file($initFlagFile) || file_get_contents($initFlagFile) != $server->manager_pid)) { // 處理專案初始化事件 initApp(); // 寫入檔案,保證不再重複觸發專案初始化事件 file_put_contents($initFlagFile, $server->manager_pid); // 當前worker進程恢復協程 resumeCos(); // 通知其它worker進程 for($i = 1; $i < $server->setting['worker_num']; ++$i) { $server->sendMessage('init', $i); } } }); $http->on('PipeMessage', function(swoole_http_server $server, $srcWorkerId, $data) { if(0 === $srcWorkerId && 'init' === $data && !defined('APP_INITED')) { // 其它worker進程恢復協程 resumeCos(); } }); $http->on('request', function (swoole_http_request $request, swoole_http_response $response) { // 判斷未初始化完畢,則掛起協程 if(!defined('APP_INITED')) { $GLOBALS['WORKER_START_END_RESUME_COIDS'][] = Coroutine::getuid(); Coroutine::suspend(); } $response->header('content-type', 'text/html;charset=utf-8'); $response->end('IMI 是一款基於 Swoole 開發的協程 PHP 開發框架,擁有常駐記憶體、協程非同步非阻塞IO等優點。官方網站:<a href="https://imiphp.com" target="_blank">https://imiphp.com</a>'); }); $http->start(); /** * 處理專案初始化事件,比如這裡延時5秒,模擬初始化處理 * * @return void */ function initApp() { $count = 5; for($i = 0; $i < $count; ++$i) { echo 'initing ', ($i + 1), '/', $count, PHP_EOL; sleep(1); } } /** * 恢復協程 * * @return void */ function resumeCos() { define('APP_INITED', true); $coids = $GLOBALS['WORKER_START_END_RESUME_COIDS'] ?? []; fwrite(STDOUT, 'suspend co count: ' . count($coids) . PHP_EOL); foreach($coids as $id) { Coroutine::resume($id); } }
通過在 request 事件中判斷是否初始化完畢,如果沒有初始化完成,則掛起當前協程,將協程ID加入全域性變數。
當第0個 worker 進程執行完初始化後,通過向其他 worker 進程傳送訊息,喚醒曾經掛起的協程們,在初始化期間進來的請求,這時候會被執行。
以上就是Swoole自定義專案初始化事件處理的實現的詳細內容,更多請關注TW511.COM其它相關文章!