乐趣区

开源-NETCore-websocket-即时通讯组件ImCore

前言

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 方法进行订阅捕捉。

结束语

谢谢支持!

退出移动版