Workerman 不中斷連線重載,平滑熱更新

回覆文章
Brave Ye
系統管理員
文章: 5547
註冊時間: 2025-11-08, 13:32
聯繫:

Workerman 不中斷連線重載,平滑熱更新

文章 Brave Ye »

要實現「不中斷 WebSocket 連線」的平滑重載 (Smooth Reload / Hot Reload),核心在於將「網路連線維護」與「業務邏輯處理」進行解耦,並遵循 Workerman 的檔案載入機制。
​一、 核心架構原理:GatewayWorker 模式
​單純的 Workerman 腳本(將 Socket 監聽與業務邏輯寫在同一個檔案)無法做到完全不中斷連線重載,因為主進程或 Worker 進程重啟時,其持有的 TCP 套接字 (Socket) 勢必會斷開。
​要實現真正的平滑熱更新,必須採用 Workerman 官方專為即時通訊設計的 GatewayWorker 架構:
​Gateway 進程 (門戶層):
​職責:專門負責維持 TCP / WebSocket 網絡連線、TLS 握手與心跳檢測。
​特性:幾乎不包含任何業務邏輯,不進行 Reload,連線永遠保持開著。
​BusinessWorker 進程 (業務層):
​職責:處理聊天室的業務邏輯(如訊息解析、廣播、資料庫讀寫、身份驗證)。
​特性:所有業務程式碼都在此層執行。當執行 Reload 命令時,GatewayWorker 會逐個平滑重啟 BusinessWorker 進程,舊進程處理完當前任務後退出,新進程載入最新的程式碼。
​二、 熱更新程式碼的撰寫規範 (極度關鍵)
​即使使用了 GatewayWorker,若程式碼撰寫方式不符合常駐記憶體的規範,Reload 也會失效。必須遵循以下 3 大原則:
​1. 業務邏輯檔案必須在 onWorkerStart 事件觸發後才動態 require/include
​如果將業務邏輯類別 (Class) 或檔案在主腳本頂層(即進程分叉 Fork 之前)就 require 進來,這些程式碼會直接載入到 Master 主進程的記憶體中,後續執行 Reload 將無法更新它們。
錯誤寫法 (無法熱更新):

代碼: 選擇全部

// 寫在檔案最上方,Master 進程啟動時就已載入記憶體
require_once __DIR__ . '/Events.php'; 

$worker = new BusinessWorker();
$worker->name = 'ChatBusinessWorker';
正確寫法 (支援熱更新):
Workerman / GatewayWorker 內部已處理好此機制,只需將業務邏輯寫在 Events.php(或指定的 EventClass)中,GatewayWorker 會在 BusinessWorker 啟動時自動載入。
2. Avoid Opcache 快чи阻礙更新
​若在 php.ini 啟用了 OPcache(生產環境通常會開啟),請確保 php.ini 設定包含以下配置,或者在執行 Reload 前清除 OPcache,否則 PHP 會繼續讀取記憶體中的舊位元組碼 (Bytecode):

代碼: 選擇全部

; 設定為 1,每次檔案修改時自動檢查並更新 OPcache
opcache.validate_timestamps=1
opcache.revalidate_freq=0
三、 執行平滑重載 (Reload) 的方法
​當你修改了 Events.php 或其他業務邏輯檔案後,切勿使用 stop 再 start,請使用以下方式進行 Reload:
​方法 1:命令列原生指令 (推薦)
​在專案根目錄下執行:

代碼: 選擇全部

php start.php reload
​運作機制:Master 進程接到 SIGUSR1 信號後,會通知 BusinessWorker 進程有序退出並重啟,Gateway 進程完全不受影響,前端 WebSocket 連線無感。
​方法 2:透過 Systemd 觸發
​若你使用 Systemd 管理 Workerman,可執行:

代碼: 選擇全部

sudo systemctl reload workerman-chat
(需確保 .service 檔案中有設定 ExecReload=/usr/bin/php /path/to/start.php reload)
​方法 3:檔案變動自動熱重載 (開發環境專用)
​在開發階段,手動輸入指令較為繁瑣。可以在專案中引進 Workerman 官方提供的 FileMonitor 模組,讓系統監控檔案修改並自動觸發 Reload:

代碼: 選擇全部

use Workerman\Worker;
use Workerman\Lib\Timer;

// 建立一個專門監控檔案變動的 Worker
$monitor = new Worker();
$monitor->name = 'FileMonitor';
$monitor->onWorkerStart = function() {
    // 監控 applications 目錄下的檔案
    $monitor_dir = __DIR__ . '/Applications';
    
    // 每秒檢查一次檔案更新時間
    Timer::add(1, function() use ($monitor_dir) {
        static $last_mtime;
        if (!$last_mtime) {
            $last_mtime = time();
            return;
        }
        
        // 遞迴檢查目錄下是否有檔案被修改
        $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($monitor_dir));
        foreach ($iterator as $file) {
            if ($file->isDir()) continue;
            if ($last_mtime < $file->getMTime()) {
                echo $file . " updated, reloading...\n";
                // 送出 SIGUSR1 信號給 Master 進程觸發 reload
                posix_kill(posix_getppid(), SIGUSR1);
                $last_mtime = time();
                break;
            }
        }
    });
};
四、 如何驗證平滑重載成功?
1.前端打開瀏覽器建立 WebSocket 聊天室連線,並監聽 onclose 與 onerror 事件:

代碼: 選擇全部

const ws = new WebSocket('wss://yourdomain.com/ws');
ws.onclose = () => console.log('連線斷開!'); // 若跳出此訊息代表平滑失敗
2修改 Events.php 中的訊息回應邏輯(例如加入新的文字字串)。
3在伺服器執行 php start.php reload
4 在前端發送訊息,若收到新的回應邏輯,且瀏覽器控制台沒有觸發 onclose,即代表平滑熱更新成功!
回覆文章

誰在線上

正在瀏覽這個版面的使用者:沒有註冊會員 和 0 位訪客