IM 能力

IM 能力的建设对公司是非常有价值的,很多项目需要实时的消息推送服务,那么历史上我们都是采购网易云信、腾讯云 IM 等,一方面价格高,最低也需要 1500 元/月;一方面问题难以排查,例如丢失消息等情况,只能提交工单解决。通过自建 IM 能力,我们具备了消息实时推送服务,进一步提升了团队快速开发项目的能力。

版本记录

IM 能力由:高哥、智强、TerryQi、侯新月贡献,有问题可以 call TerryQi

版本更新时间备注
1.0.02022 年 07 月 01 日原始版本,来自于的士同城项目,抽象了消息单发、群发等服务
2.0.02023 年 12 月 15 日由侯新月主导,支持了市场调查项目,完成基本的消息发送、持久化等操作
3.0.02023 年 12 月 24 日正式优化版本,作为一个基础能力,面向团队提供公共服务

OpenIM(重点推荐)

IM 是一个重要的应用场景,历史上我们没有找到好的开源项目,所以自研了一套 IM 平台,但还是刚刚起步,很多功能不具备。

2024 年 2 月,青青找到了一款开源的 IM 能力——OpenIMopen in new window,提供了各个端的 SDK,他仅仅使用一周的时间,就实现了一套单聊和群组沟通功能,那么我们就应该与时俱进,建议后续使用 OpenIM 来提供服务。

非常感谢青青为大家找到这个能力,为团队做出贡献。

代码位置

IM 能力的代码位置

https://gitee.com/qrqy/im-app

请注意,由于项目使用 gradle 进行编写,所以要在 idea 中配置 gradle,用 bootjar 进行打包

  1. 在 idea 中,配置 gradle 版本。请注意,可能选择不一样的 gradle 版本,比较稳定的貌似是 7.4.0 版本

gradle配置

  1. 配置文件编码,不知道貌似与第 5 点有关系,打包时要配置编码格式

gradle配置

gradle配置

  1. 使用 bootjar 进行打包

gradle配置

  1. 如果还是乱码,配置 idea 的 vm 虚拟机

gradle配置

整体架构

IM 目前是单机版本,使用 Netty 、WebFlux 实现 Websocket 和 Http 请求管理

gradle配置

如何使用

我们基本的思考是这样,我们要搭建一个 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 的模拟测试

gradle配置

IM 的两个机制

首先,要说明的是目前 IM 平台提供了两种鉴权机制:

  1. 不需要应用鉴权

应用模拟用户,发送登陆申请,传送 token,则默认进行登陆,系统以这个 token 作为 userId,后续通过这个 token 发送消息。这样的机制没有什么安全性,只要抓取到报文,任何人都可以接入 IM 应用。

PS:这里要强调一点,这个方式虽然安全性差,但很多场景下也是一个好的方案,例如我们的业务不太需要安全性或者没有价值,不担心攻击,那么这个方案可以快速引入 IM 能力,非常直观和便捷——不是安全性差的方案都是坏方案,要结合你的业务场景来考量!

  1. 需要应用鉴权

用户登陆后,IM 回调应用鉴权接口,进行登陆鉴权,鉴权成功后,IM 应用登陆成功。这样的方案安全性会更好一些,需要应用按照规范,向 IM 提供一个登陆鉴权接口。

那么怎么定义应用是不是需要鉴权呢?这里是 TerryQi 在分配应用的时候,配置的属性。

gradle配置

不需要应用鉴权方案

首先,提供一套 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应用

此外,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 就会向你发送心跳,接入方要时刻检查心跳,超时后发起重连和重新登陆的操作

IM应用

Http 接口服务

目前提供了一丢丢基础消息能力,主要是:消息发送、用户在线状态查询,当然 IM3.0 中还是历史消息持久化的能力,稍后开放

IM应用

向用户发送消息

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:消息对象,自由封装

IM应用

批量向用户发送消息

区别点在于 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 登陆接口,完成鉴权

IM 的登陆报文没有啥区别,但是系统会回调应用进行鉴权,典型的信息是 userId 为应用的 uuid,不再是 token 了

{
  "body": {
    "appId": "20231224143",
    "token": "0b06ddba3be4429c923d11595e4ec33c"
  },
  "header": {
    "command": "SOCKET_LOGIN",
    "reqNo": "wMkuUCHcalnUEgnFQymdBwwVIpDVnfij",
    "version": "1.0"
  },
  "time": 1702266156633
}

IM应用

下一步工作计划

IM3.0 只是简单的开始,IM 还应有:

  • 历史消息存储和查询
  • 离线消息暂存和发送
  • 点对点消息发送
  • 消息发送状态记录和回执
  • 消息是否成功接收记录和回执
  • ...

IM 能力将会是公司持续优化提升的基础能力

Last Updated:
Contributors: TerryQi