laravel-echo-server-disconnectable
v1.7.0
Published
Laravel Echo Node JS Server for Socket.io
Readme
laravel-echo-server-disconnectable
laravel-echo-server 的 fork。
在原版的基礎上加入兩類能力:
- 可從伺服器端關閉指定的 socket 連線(HTTP API)。
- 多節點(多台 node 共用同一個 Redis)下的 presence channel 一致性——名單不會因為跨節點而掉人,進出通知也能跨節點推播。
套件名:
laravel-echo-server-disconnectable,CLI 指令同名。用法與設定(laravel-echo-server.json)與上游相同,差異僅在下列新增 / 修正。
一、關閉連線 API(disconnectable 功能)
關閉 socket 連線
GET /apps/:APP_ID/sockets/:SOCKET_ID/destroy/channels/:CHANNEL_NAMESOCKET_ID可從前端Echo.socketId();取得。CHANNEL_NAME用來進行 leave 動作,以便觸發 presence 名單更新(PresenceChannelUpdated)。- 找不到該 socket 時回傳
400 { "error": "Socket id is not exists!" }。
⚠️ 多節點限制:此 API 目前只會在「收到請求的那一台 node」上尋找 socket。若 socket 連在別台 node,會回
Socket id is not exists!。在多節點部署下,請確保請求被導到承載該 socket 的節點,否則無法踢除。(跨節點踢人需另以 Redis 指令廣播實作,尚未包含。)
列出所有 socket id
GET /apps/:APP_ID/sockets["DyjRDrMKePecLHqOAAAO", "pdJ7IinrB8ZMH18aAAAC", "x-av0NW1ZXoexUKWAAAJ"]取得 channel 資訊(response 含 sockets)
GET /apps/:APP_ID/channels/:CHANNEL_NAME{
"subscription_count": 3,
"occupied": true,
"user_count": 3,
"sockets": ["pdJ7IinrB8ZMH18aAAAC", "DyjRDrMKePecLHqOAAAO", "x-av0NW1ZXoexUKWAAAJ"]
}取得 channel 上的使用者(response 含 socketId)
GET /apps/:APP_ID/channels/:CHANNEL_NAME/users[
{
"user_id": 1,
"user_info": { "id": 1, "name": "Karick", "job_title": null },
"socketId": "DyjRDrMKePecLHqOAAAO"
},
{
"user_id": 2,
"user_info": { "id": 2, "name": "Eason", "job_title": null },
"socketId": "pdJ7IinrB8ZMH18aAAAC"
}
]
user_count與users來自共用的 Redis 名單,已是跨節點一致的(見下)。subscription_count/sockets仍是「本機節點」的連線視角。
二、多節點 presence 一致性
解決的問題
presence 名單存在共用的 Redis(<channel>:members)。原版有一段 removeInactive(),會用 io.of("/").in(channel).clients()(只看得到本機這台 node 的連線,因為未掛 socket.io redis adapter)去過濾名單,再整包覆寫回 Redis——於是一台 node 會把「連在別台 node 的活成員」當成不存在而刪掉。兩台互刪的結果,就是不同節點看到的 members 不一致、人數忽多忽少。
做法
- 名單改為 Redis Hash:
<channel>:members以socketId為 field,每條 socket 各佔一格。join 只HSET自己、leave / 斷線只HDEL自己——沒有任何節點會再動到別人的格子,誤刪與並發競態一併消失。 - 移除
removeInactive():跨節點本就無法用本機視角安全判定誰還活著;改 Hash 後每條 socket 在斷線時自行清掉,不再需要整包對帳。 - 進出 / whisper 事件走既有 Redis bus:
presence:joining/presence:leaving與 client 事件(whisper)改成 publish 到 Laravel 既有的 Redis bus,由每台 node 的 subscriber 各自本機 emit 一次。如此即可跨節點,且不需要socket.io-redisadapter(用 adapter 反而會讓每則 Laravel 事件被重複投遞 N 次)。 - 舊資料自動遷移:升級後首次讀到舊版 JSON 陣列字串時,會就地遷移成 hash;transient redis 錯誤(連線中斷等)只記錄、絕不刪除 key。
設定
| 選項 | 預設 | 說明 |
|---|---|---|
| databaseConfig.redis.busBroadcast | true(redis 模式) | 是否把 presence/whisper 事件透過 Redis bus 跨節點廣播。單機部署可設為 false 退回「直接本機 emit」,省一次 Redis round-trip。 |
| databaseConfig.redis.publishPresence | false | (沿用)名單變動時額外 publish PresenceChannelUpdated,供外部監聽。 |
跨節點事件廣播需要 Redis subscriber 啟用(
subscribers.redis,預設開啟)。database為sqlite(單機)時自動使用本機 emit。
三、開發 / 測試
npm install
npm run build # tsc 編譯到 dist/
npm test # mocha + chai + ts-node測試涵蓋:Redis hash 與舊資料遷移、presence join/leave(含並發離線、寫入失敗)、跨節點事件廣播的 envelope、以及 disconnect API 合約。
注意:跨節點的「實際送達」需在 ≥2 台 node + 共用 Redis 的真實環境才能完整驗收;單元測試覆蓋的是邏輯與訊息格式。建議上線後以兩個帳號分別連不同節點,實測 presence 進出與 whisper。
需求
- Node.js >= 18
- socket.io 2.x(與 laravel-echo 前端相容)
- 多節點時:所有節點共用同一個 Redis
