IM 能力
IM 能力的建设对公司是非常有价值的,很多项目需要实时的消息推送服务,那么历史上我们都是采购网易云信、腾讯云 IM 等,一方面价格高,最低也需要 1500 元/月;一方面问题难以排查,例如丢失消息等情况,只能提交工单解决。通过自建 IM 能力,我们具备了消息实时推送服务,进一步提升了团队快速开发项目的能力。
版本记录
IM 能力由:高哥、智强、TerryQi、侯新月贡献,有问题可以 call TerryQi
版本 | 更新时间 | 备注 |
---|---|---|
1.0.0 | 2022 年 07 月 01 日 | 原始版本,来自于的士同城项目,抽象了消息单发、群发等服务 |
2.0.0 | 2023 年 12 月 15 日 | 由侯新月主导,支持了市场调查项目,完成基本的消息发送、持久化等操作 |
3.0.0 | 2023 年 12 月 24 日 | 正式优化版本,作为一个基础能力,面向团队提供公共服务 |
OpenIM(重点推荐)
IM 是一个重要的应用场景,历史上我们没有找到好的开源项目,所以自研了一套 IM 平台,但还是刚刚起步,很多功能不具备。
2024 年 2 月,青青找到了一款开源的 IM 能力——OpenIM,提供了各个端的 SDK,他仅仅使用一周的时间,就实现了一套单聊和群组沟通功能,那么我们就应该与时俱进,建议后续使用 OpenIM 来提供服务。
非常感谢青青为大家找到这个能力,为团队做出贡献。
代码位置
IM 能力的代码位置
https://gitee.com/qrqy/im-app
请注意,由于项目使用 gradle 进行编写,所以要在 idea 中配置 gradle,用 bootjar 进行打包
- 在 idea 中,配置 gradle 版本。请注意,可能选择不一样的 gradle 版本,比较稳定的貌似是 7.4.0 版本
- 配置文件编码,不知道貌似与第 5 点有关系,打包时要配置编码格式
- 使用 bootjar 进行打包
- 如果还是乱码,配置 idea 的 vm 虚拟机
整体架构
IM 目前是单机版本,使用 Netty 、WebFlux 实现 Websocket 和 Http 请求管理
如何使用
我们基本的思考是这样,我们要搭建一个 PAAS 级的 IM 能力平台,可以让应用接入,这样的好处是对于一些小项目,我们就不需要设立多套 IM 服务对应于应用,而是各个应用接入 PAAS 的 IM 能力平台。
接入准备
就像你要接入网易云信一样,先找 TerryQi 分配 app-id 和 secret-key
在 platform_auth 表中,分配 appId 和 appSecret,目前没有管理后台,TerryQi 会在数据库中为您配置账号
目前提供 IM 平台环境为:
- WebSocket 接口:
wss://im-ws.wltwo112.yisa.art/ws/server
- API 接口:
https://im-api.wltwo112.yisa.art
你需要打开http://www.websocket-test.com/
,可以进行 websocket 的模拟测试
IM 的两个机制
首先,要说明的是目前 IM 平台提供了两种鉴权机制:
应用模拟用户,发送登陆申请,传送 token,则默认进行登陆,系统以这个 token 作为 userId,后续通过这个 token 发送消息。这样的机制没有什么安全性,只要抓取到报文,任何人都可以接入 IM 应用。
PS:这里要强调一点,这个方式虽然安全性差,但很多场景下也是一个好的方案,例如我们的业务不太需要安全性或者没有价值,不担心攻击,那么这个方案可以快速引入 IM 能力,非常直观和便捷——不是安全性差的方案都是坏方案,要结合你的业务场景来考量!
- 需要应用鉴权
用户登陆后,IM 回调应用鉴权接口,进行登陆鉴权,鉴权成功后,IM 应用登陆成功。这样的方案安全性会更好一些,需要应用按照规范,向 IM 提供一个登陆鉴权接口。
那么怎么定义应用是不是需要鉴权呢?这里是 TerryQi 在分配应用的时候,配置的属性。
不需要应用鉴权方案
首先,提供一套 app-id 和 sercret-key,这个是无需应用鉴权的应用
app-id: 20240102120
app-secret:bKc8y6c1w8iR725h
WebSocket-用户登陆报文
{
"body": {
"appId": "20240102120",
"token": "0b06ddba3be4429c923d11595e4ec33c"
},
"header": {
"command": "SOCKET_LOGIN",
"reqNo": "wMkuUCHcalnUEgnFQymdBwwVIpDVnfij",
"version": "1.0"
},
"time": 1702266156633
}
解释一下报文:
- body:消息体。appId 为 TerryQi 分配的应用 id;token 约等于是 userId,因为属于无鉴权方案,则 IM 使用 token 作为用户 id 进行注册
- header:消息头。command:必须是 SOCKET_LOGIN,代表登陆 IM;reqNo:由前端生成,是唯一的流水号,特殊解释一下,如果请求消息量特别大,那么我们需要前端发出的消息和服务端返回的消息可以一一对应,如何对应,就依赖于 reqNo;verison:默认 1.0
- time:时间戳
连接 IM 后,就可以发送登陆请求
此外,IM 目前还有房间能力,那么目前只允许用户加入一个房间,在登陆报文中,增加 roomId 字段即可,未来向 roomId 发送消息,则在房间里面的人都会接到消息
{
"body": {
"appId": "20240102120",
"token": "0b06ddba3be4429c923d11595e4ec33c",
"roomId": "ROOM-603428310"
},
"header": {
"command": "SOCKET_LOGIN",
"reqNo": "wMkuUCHcalnUEgnFQymdBwwVIpDVnfij",
"version": "1.0"
},
"time": 1702266156633
}
请注意,可以多次登陆,例如用户首次登陆,没有加入房间,点击某个按钮加入房间,再发送一下 SOCKET_LOGIN 指令,是没有问题的
心跳机制
为了有效控制 IM 的连接,IM 平台提供了心跳机制:
- 你连接了 IM,但是没有进行登陆操作,那么 60s 后,IM 自动断掉这样无效的连接
- 如果你进行了 IM 的登陆,那么 IM 每隔 30s 就会向你发送心跳,接入方要时刻检查心跳,超时后发起重连和重新登陆的操作
Http 接口服务
目前提供了一丢丢基础消息能力,主要是:消息发送、用户在线状态查询,当然 IM3.0 中还是历史消息持久化的能力,稍后开放
向用户发送消息
curl --location --request POST 'https://im-api.wltwo112.yisa.art/api/message/sendToUser' \
--header 'User-Agent: Apifox/1.0.0 (https://apifox.com)' \
--header 'Content-Type: application/json' \
--header 'Accept: */*' \
--header 'Host: im-api.wltwo112.yisa.art' \
--header 'Connection: keep-alive' \
--data-raw '{
"appId": "20240102120",
"toUserId": "0b06ddba3be4429c923d11595e4ec33c",
"message": {
"test": "TerryQi Say Hello"
}
}'
参数为:
- appId:应用 id
- toUserId:用户 id,无鉴权模式下,用户 id 就是 token
- message:消息对象,自由封装
批量向用户发送消息
区别点在于 toUserId 变为 toUserIdList,数组形式,传入 userId
curl --location --request POST 'https://im-api.wltwo112.yisa.art/api/message/sendToUserList' \
--header 'User-Agent: Apifox/1.0.0 (https://apifox.com)' \
--header 'Content-Type: application/json' \
--header 'Accept: */*' \
--header 'Host: im-api.wltwo112.yisa.art' \
--header 'Connection: keep-alive' \
--data-raw '{
"appId": "20231224143",
"toUserIdList": ["4046a9bc9c7945a086c84f0239d53d4c","ce64685b2aba4ad2ab11d55c972290e3"],
"message": {
"test": "hello world,TerryQi"
}
}'
向房间发送消息
toUserId 变为 toRoomId,请注意,要求用户登陆时需要填充 roomId
curl --location --request POST 'https://im-api.wltwo112.yisa.art/api/message/sendToRoom' \
--header 'User-Agent: Apifox/1.0.0 (https://apifox.com)' \
--header 'Content-Type: application/json' \
--header 'Accept: */*' \
--header 'Host: im-api.wltwo112.yisa.art' \
--header 'Connection: keep-alive' \
--data-raw '{
"appId": "20231224143",
"toRoomId": "Room-9721234",
"message": {
"test": "hello world,TerryQi"
}
}'
查询应用全部在线用户
查询在线用户列表,传入应用 id
curl --location --request POST 'https://im-api.wltwo112.yisa.art/api/user/allOnline' \
--header 'User-Agent: Apifox/1.0.0 (https://apifox.com)' \
--header 'Content-Type: application/json' \
--header 'Accept: */*' \
--header 'Host: im-api.wltwo112.yisa.art' \
--header 'Connection: keep-alive' \
--data-raw '{
"appId": "20231224143"
}'
查询在线用户
传入用户 id 列表,查询在线状态
curl --location --request POST 'https://im-api.wltwo112.yisa.art/api/user/checkOnline' \
--header 'User-Agent: Apifox/1.0.0 (https://apifox.com)' \
--header 'Content-Type: application/json' \
--header 'Accept: */*' \
--header 'Host: im-api.wltwo112.yisa.art' \
--header 'Connection: keep-alive' \
--data-raw '{
"appId": "20231224143",
"userIdList": [
"userid-1",
"userid-2"
]
}'
需要应用鉴权的方案
通过应用鉴权,IM 的安全性会高一些,提供一套需要应用鉴权的 app-id 和 app-secret
app-id: 20231224143
app-secret:eiYwzztaVPEdKUyx
提供一些用户 token,供你测试
用户A:0b06ddba3be4429c923d11595e4ec33c
用户B:c18886f3bf1d4cadbd5d3e57ac585c10
用户C:ce64685b2aba4ad2ab11d55c972290e3
这个应用鉴权的 auth-api:
curl --location --request POST 'https://api.market-survey.wltwo107.qrqy.net/api/common/im/user/login' \
--header 'Authorization: 0b06ddba3be4429c923d11595e4ec33c' \
--header 'User-Agent: Apifox/1.0.0 (https://apifox.com)' \
--header 'Content-Type: application/json' \
--header 'Accept: */*' \
--header 'Host: api.market-survey.wltwo107.qrqy.net' \
--header 'Connection: keep-alive' \
--data-raw '{
"token": "0b06ddba3be4429c923d11595e4ec33c"
}'
所谓鉴权型 IM 应用就需要你按照上面报文格式,封装一个 IM 的用户登陆接口,那么当用户登陆 IM 时,IM 平台回调应用的 IM 登陆接口,完成鉴权
IM 的登陆报文没有啥区别,但是系统会回调应用进行鉴权,典型的信息是 userId 为应用的 uuid,不再是 token 了
{
"body": {
"appId": "20231224143",
"token": "0b06ddba3be4429c923d11595e4ec33c"
},
"header": {
"command": "SOCKET_LOGIN",
"reqNo": "wMkuUCHcalnUEgnFQymdBwwVIpDVnfij",
"version": "1.0"
},
"time": 1702266156633
}
下一步工作计划
IM3.0 只是简单的开始,IM 还应有:
- 历史消息存储和查询
- 离线消息暂存和发送
- 点对点消息发送
- 消息发送状态记录和回执
- 消息是否成功接收记录和回执
- ...
IM 能力将会是公司持续优化提升的基础能力