头图

WebSocket User Message Center

Using websocket as a message center is usually implemented with middleware such as kafka and redis. Using CONNMIX does not need to use middleware, and the distributed cluster capability does not need to worry about performance problems caused by a large increase in the number of users.

Require

Design ideas

  • The client sends a message to log in after the ws connection is successful, uses lua to call the business api interface to parse the uid in the login token data, and then saves the uid in the connection context.
  • After the login is successful, send a message to subscribe to a channel with a user ID user:<uid> , and the uid is taken from the context.
  • In the interface for sending user messages, call the /v1/mesh/publish interface of any node in connmix to the corresponding uid to send real-time messages, and all ws clients that subscribe to the channel will receive the message.
  • The above are all incremental push designs, and the full amount is usually obtained through a full amount api interface when the page is loaded.

Interactive protocol design

  • You must be logged in to subscribe, unsubscribe
  • When the user sends @user we do subscribing to the user:\<uid\> channel in the lua code.
Function json format
Log in {"op":"auth","token":" * "}
Subscribe to user news {"op":"subscribe","channel":"@user"}
Cancel user message {"op":"unsubscribe","channel":"@user"}
User message events {"event":"@user","data":{"uid":1001,"msg":"Hello,World!"}}
success {"result":true}
mistake {"code":1,"msg":"Error"}

Install the engine

Change setting

In the connmix.yaml configuration file options option, modify the url path of the websocket

 options:
  - name: path
    value: /message-center

CONNMIX encoding

Modify the entry.websocket.lua on_message method of ---98978f813922f9382ac7c73f71f49333--- as follows:

  • When the message is of auth type, call auth_url interface to get the uid through the token and save it to the context
  • When the message is subscribe or unsubscribe, the uid is taken out from the context, and the channel corresponding to the subscription/unsubscription is executed.
 function on_message(msg)
    --print(msg)
    if msg["type"] ~= "text" then
        conn:close()
        return
    end

    local auth_url = "http://127.0.0.1:8000/websocket_auth" --填写解析token的api接口地址
    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 channel_type = channel_table[2]

    if op == "auth" then
        local token = data["token"]
        local resp, err = mix.http.request("POST", auth_url, {
            body = '{"token:"' .. token .. '"}'
        })
        if err then
            mix_log(mix_DEBUG, "http.request error: " .. err)
            conn:close()
            return
        end
        if resp.status_code ~= 200 then
            mix_log(mix_DEBUG, "http.request status_code: " .. resp["status_code"])
            conn:close()
            return
        end
        local body_table, err = mix.json_decode(resp["body"])
        if err then
            mix_log(mix_DEBUG, "json_decode error: " .. err)
            conn:close()
            return
        end
        conn:set_context_value("uid", body_table["uid"])
        return
    end

    local uid = conn:context_value("uid")

    if op == "subscribe" and channel_type == "user" then
        if uid == nil then
            conn:send('{"code":1,"msg":"Not Auth"}')
            return
        end
        local err = conn:subscribe("user:" .. uid)
        if err then
            mix_log(mix_DEBUG, "subscribe error: " .. err)
            conn:close()
            return
        end
    end

    if op == "unsubscribe" and channel_type == "user" then
        if uid == nil then
            conn:send('{"code":1,"msg":"Not Auth"}')
            return
        end
        local err = conn:unsubscribe("user:" .. uid)
        if err then
            mix_log(mix_DEBUG, "unsubscribe error: " .. err)
            conn:close()
            return
        end
    end

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

API encoding

Write a login information verification interface in the framework of the existing system /websocket_auth for ws login to obtain user uid

  • Interface input parameter: token
 {"token":"***"}
  • Interface output parameter: uid
 {"uid":1001}

Implement proactive message push in the framework of existing systems

  • You can write an interface for sending user messages in spring and laravel frameworks
  • After verifying the user's identity in this interface, execute the following http request to complete the push
  • If sending requests are very frequent, you can use websocket-api push instead to improve performance
 curl --request POST 'http://127.0.0.1:6789/v1/mesh/publish' \
--header 'Content-Type: application/json' \
--data-raw '{
    "c": "user:1001",
    "d": "{\"event\":\"@user\",\"data\":{\"uid\":1001,\"msg\":\"Hello,World!\"}}"
}'

test

Test with wstool

  • connect ws://127.0.0.1:6790/message-center
  • send {"op":"auth","token":"***"}
  • Received {"result":true}
  • send {"op":"subscribe","channel":"@user"}
  • Received {"result":true}
  • Execute curl active push
  • Received {"event":"@user","data":{"uid":1001,"msg":"Hello,World!"}}

撸代码的乡下人
252 声望46 粉丝

我只是一个单纯爱写代码的人,不限于语言,不限于平台,不限于工具