前言
ImCore 是一款 .NETCore 下利用 WebSocket 实现的简易、高性能、集群即时通讯组件,支持点对点通讯、群聊通讯、上线下线事件消息等众多实用性功能。
开源地址:https://github.com/2881099/im,求 star~~
快速开始
dotnet add package ImCore
IM 服务端
public void Configure(IApplicationBuilder app)
{
app.UseImServer(new ImServerOptions
{Redis = new CSRedis.CSRedisClient("127.0.0.1:6379,poolsize=5"),
Servers = new[] { "127.0.0.1:6001"}, // 集群配置
Server = "127.0.0.1:6001"
});
}
一套永远不需要迭代更新的 IM 服务端
WebApi 业务端
public void Configure(IApplicationBuilder app)
{
//...
ImHelper.Initialization(new ImClientOptions
{Redis = new CSRedis.CSRedisClient("127.0.0.1:6379,poolsize=5"),
Servers = new[] { "127.0.0.1:6001"}
});
ImHelper.EventBus(t => Console.WriteLine(t.clientId + "上线了"),
t => Console.WriteLine(t.clientId + "下线了"));
}
ImHelper 方法 | 参数 | 描述 |
---|---|---|
PrevConnectServer | (clientId, string) | 在终端准备连接 WebSocket 前调用 |
SendMessage | (发送者, 接收者, 消息内容, 是否回执) | 发送消息 |
GetClientListByOnline | – | 返回所有在线 clientId |
EventBus | (上线委托, 离线委托) | socket 上线与下线事件 |
群聊频道 | 参数 | 描述 |
---|---|---|
JoinChan | (clientId, 频道名) | 加入 |
LeaveChan | (clientId, 频道名) | 离开 |
GetChanClientList | (频道名) | 获取群聊频道所有 clientId |
GetChanList | – | 获取所有群聊频道和在线人数 |
GetChanListByClientId | (clientId) | 获取用户参与的所有群聊频道 |
GetChanOnline | (频道名) | 获取群聊频道的在线人数 |
SendChanMessage | (clientId, 频道名, 消息内容) | 发送群聊消息,所有在线的用户将收到消息 |
说明:clientId 应该与 webApi 的用户 id 相同,或者有关联。
Html5 终端
本方案支持集群分区,前端连接 websocket 前,应该先请求 webApi 获得地址(ImHelper.PrevConnectServer)。
运行示例
运行环境:.NETCore 2.1 + redis-server 2.8
下载 Redis-x64-2.8.2402.zip,点击 start.bat 运行;
cd imServer && dotnet run
cd web && dotnet run
打开多个浏览器,访问 http://127.0.0.1:5000 发送群消息
设计思路
imServer 是 websocket 服务中心,可部署多实例,按 clientId 分区管理 socket 连接;
webApi 或其他应用端,使用 ImHelper 调用相关方法(如:SendMessage、群聊相关方法);
消息发送利用了 redis 订阅发布技术。每个 imServer 订阅相应的频道,收到消息,指派 websocket 向终端(如浏览器)发送消息;
1、可缓解并发推送消息过多的问题;
2、可解决连接数过多的问题;
客户端连接流程:client -> websocket -> imserver
imserver 订阅消息:client <- imserver <- redis channel
推送消息流程:web1 -> sendmsg 方法 -> redis channel -> imserver
imserver 充当消息转发,及维护连接中心,代码万年不变不需要重启维护;
WebSocket
比较笨的办法是浏览器端使用 websocket,其他端 socket,这种混乱的设计非常难维护。
强烈建议所有端都使用 websocket 协议,adorid/ios/h5/ 小程序全部支持 websocket 客户端。
业务与通讯协议
im 系统一般涉及【我的好友】、【我的群】、【历史消息】等等。。
那么,imServer 与业务方 (webApi) 该保持何种关系呢?
用户 A 向好友 B 发送消息,分析一下:
- 需要判断 B 是否为 A 好友;
- 需要判断 A 是否有权限;
- 等等。。
诸如此类业务判断会很复杂,我们试想一下,如果使用 imServer 做业务协议,它是不是会变成巨无霸难以维护?
又比如获取历史聊天记录,难道客户端要先 websocket.send(‘gethistory’),再在 onmessage 里定位回调处理?
我们可以这样设定,所有用户的主动行为走业务方(webApi),imServer 只负责即时消息推送。什么意思?
用户 A 向好友 B 发送消息:客户端请求业务方 (webApi) 接口,由业务方 (webApi) 后端向 imServer 发起推送请求,imServer 收到指令后,向前端用户 B 的 websocket 发送数据,用户 B 收到了消息。
获取历史消息:客户端请求业务方 (webApi) 接口,返回 json(历史消息)
回执:用户 A 如何知道消息发送状态(成功或失败或不在线)?imServer 端向用户 B 发送消息时,把状态以消息的方式推给用户 A 即可(按上面的逻辑),具体请看源码吧。。。
发送消息
采用 redis 轻量级的订阅发布功能,实现消息缓冲发送。
集群分区
单个 imServer 实例支持多少个客户端连接,两千个没问题?
如果在线用户有 10 万人,怎么办???
比如部署 4 个 imServer:
imServer1 订阅 redisChanne1
imServer2 订阅 redisChanne2
imServer3 订阅 redisChanne3
imServer4 订阅 redisChanne4
业务方 (webApi) 端根据接收方的 clientId 后四位 16 进制与节点总数取模,定位到对应的 redisChannel,进行 redis->publish 操作将消息定位到相应的 imServer。
每个 imServer 管理着对应的终端连接,当接收到 redis 订阅消息后,向对应的终端连接推送数据。
事件消息
IM 系统比较常用的有上线、下线,在 imServer 层才能准确捕捉事件,但业务代码就不合适在这上面编写了。
采用 redis 发布订阅技术,将上线、下线等事件向指定频道发布,业务方(webApi) 通过 ImHelper.EventBus 方法进行订阅捕捉。
结束语
谢谢支持!