WebSocket 用户音讯核心
应用 websocket 做音讯核心,通常做法是采纳 kafka、redis 等中间件搭配实现,应用 CONNMIX 则无需应用中间件,同时分布式集群能力也无需担心用户量大增后带来的性能问题。
要求
- connmix >= v1.0.4
设计思路
- 客户端在 ws 连贯胜利后发送音讯执行登录,应用 lua 调用业务 api 接口解析登录 token 数据中的 uid,而后将 uid 保留到连贯的 context 中。
- 登录胜利后发送音讯订阅一个用户 ID 的通道
user:<uid>
,该 uid 从 context 中取出。 - 在发送用户音讯的接口中,调用 connmix 任意节点的
/v1/mesh/publish
接口往对应uid
发送实时音讯,所有订阅该通道的 ws 客户端都将会收到音讯。 - 以上都是增量推送设计,全量通常都是在页面加载时通过一个全量 api 接口获取。
交互协定设计
- 必须登录后能力执行订阅、勾销订阅
- 当用户发送 @user 咱们在 lua 代码中就执行订阅 user:\<uid\> 通道。
性能 | json 格局 |
---|---|
登录 | {“op”:”auth”,”token”:”*“} |
订阅用户音讯 | {“op”:”subscribe”,”channel”:”@user”} |
勾销用户音讯 | {“op”:”unsubscribe”,”channel”:”@user”} |
用户音讯事件 | {“event”:”@user”,”data”:{“uid”:1001,”msg”:”Hello,World!”}} |
胜利 | {“result”:true} |
谬误 | {“code”:1,”msg”:”Error”} |
装置引擎
connmix
https://connmix.com/docs/1.0/#/zh-cn/install-engine
批改配置
在 connmix.yaml
配置文件的 options
选项,批改 websocket 的 url 门路
options:
- name: path
value: /message-center
CONNMIX 编码
批改 entry.websocket.lua
的 on_message
办法如下:
- 当音讯为 auth 类型时,调用
auth_url
接口通过 token 获取到 uid,并保留到 context 中 - 当音讯为 subscribe、unsubscribe 时,从 context 取出 uid,执行订阅 / 勾销订阅对应的通道
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 编码
在现有零碎的框架中编写一个登录信息验证接口 /websocket_auth
,用于 ws 登录获取用户 uid
- 接口入参:token
{"token":"***"}
- 接口出参:uid
{"uid":1001}
在现有零碎的框架中实现被动音讯推送
- 能够在 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":"user:1001","d":"{\"event\":\"@user\",\"data\":{\"uid\":1001,\"msg\":\"Hello,World!\"}}"}'
测试
应用 wstool 进行测试
- 连贯
ws://127.0.0.1:6790/message-center
- 发送
{"op":"auth","token":"***"}
- 接管到
{"result":true}
- 发送
{"op":"subscribe","channel":"@user"}
- 接管到
{"result":true}
- 执行 curl 被动推送
- 接管到
{"event":"@user","data":{"uid":1001,"msg":"Hello,World!"}}