CONNMIX 開發 WebSocket 影片彈幕

onanying發表於2022-09-06

使用WebSocket製作一個單機版彈幕系統非常簡單,但是當單機效能達到瓶頸,需要擴充套件為叢集部署時就會面臨很多分散式問題,使用CONNMIX則無需擔心這些問題,很少的程式碼即可完成一個高效能分散式WebSocket叢集。

要求

設計思路

  • 客戶端在ws連線成功後,訂閱一個影片ID的通道 video:<videoid>
  • 在傳送彈幕的介面中,呼叫connmix任意節點的 /v1/mesh/publish 介面往對應 video_id 傳送實時彈幕,所有訂閱該通道的ws客戶端都將會收到訊息。
  • 以上都是增量推送設計,全量通常都是在頁面載入時透過一個全量api介面獲取。

互動協議設計

  • 當使用者傳送 100001@video 我們在 lua 程式碼中就執行訂閱 video:100001 通道。
  • 前端切換影片時,可先傳送取消彈幕訊息,然後傳送新的訂閱彈幕訊息。
功能 json格式
訂閱彈幕 {“op”:”subscribe”,”channel”:”100001@video”}
取消彈幕 {“op”:”unsubscribe”,”channel”:”100001@video”}
彈幕事件 {“event”:”@video”,”data”:{“uid”:1001,”video_id”:100001,”msg”:”Hello,World!”}}
成功 {“result”:true}
錯誤 {“code”:1,”msg”:”Error”}

安裝引擎

修改配置

connmix.yaml 配置檔案的 options 選項,修改websocket的url路徑

options:
  - name: path
    value: /barrage-videos

CONNMIX 編碼

修改 entry.websocket.luaon_message 方法如下:

  • 當接受到subscribe、unsubscribe訊息時,執行訂閱/取消訂閱對應的通道
function on_message(msg)
    --print(msg)
    if msg["type"] ~= "text" then
        conn:close()
        return
    end

    local conn = mix.websocket()

    local data, err = mix.json_decode(msg["data"])
    if err then
        mix_log(mix_DEBUG, "json_decode error: " .. err)
        conn:close()
        return
    end

    local op = data["op"]
    local channel_raw = data["channel"]
    local channel_table = mix.str_split(channel_raw, "@")
    if table.getn(channel_table) ~= 2 then
        mix_log(mix_DEBUG, "invalid channel: " .. channel_raw)
        conn:close()
        return
    end
    local video_id = channel_table[1] --Lua的table索引預設從1開始
    local channel_type = channel_table[2]

    if op == "subscribe" and channel_type == "video" then
        local err = conn:subscribe("video:" .. video_id)
        if err then
            mix_log(mix_DEBUG, "subscribe error: " .. err)
            conn:close()
            return
        end
    end

    if op == "unsubscribe" and channel_type == "video" then
        local err = conn:unsubscribe("video:" .. video_id)
        if err then
            mix_log(mix_DEBUG, "unsubscribe error: " .. err)
            conn:close()
            return
        end
    end

    conn:send('{"result":true}')
end

API 編碼

接下來在現有系統的框架中實現主動彈幕推送

  • 可以在 spring、laravel 框架中寫一個傳送彈幕介面
  • 該介面中驗證完使用者身份後,執行以下http請求完成推送
  • 如果傳送請求非常頻繁,可以改用 websocket-api推送 提升效能
curl --request POST 'http://127.0.0.1:6789/v1/mesh/publish' \
--header 'Content-Type: application/json' \
--data-raw '{
    "c": "video:100001",
    "d": "{\"event\":\"@video\",\"data\":{\"uid\":1001,\"video_id\":100001,\"msg\":\"Hello,World!\"}}"
}'

測試

使用 wstool 進行測試

  • 連線 ws://127.0.0.1:6790/barrage-videos
  • 傳送 {"op":"subscribe","channel":"100001@video"}
  • 接收到 {"result":true}
  • 執行 curl 主動推送
  • 接收到 {"event":"@video","data":{"uid":1001,"video_id":100001,"msg":"Hello,World!"}}
本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章