乐趣区

关于im:开源精品-CNET-im-聊天通讯架构设计-FreeIM-支持集群职责分明高性能


💻 FreeIM 是什么?

FreeIM 应用 websocket 协定实现繁难、高性能(单机反对 5 万 + 连贯)、集群即时通讯组件,反对点对点通信、群聊通信、上线下线事件音讯等泛滥实用性性能。ImCore 已正式改名为 FreeIM

应用场景:好友聊天、群聊天、直播间、实时评论区、游戏。

FreeIM 解耦了通信与业务模块,让我的项目架构变得更加简略易保护,2017 年的设计再过 5 年也不过时。

FreeIM 提供了一套永远不须要迭代更新的 ImServer 服务端,反对 .NET5.0、.NETCore2.1+、NETStandard2.0。

以及一套简略的 ImHelper API 提供给 业务端 应用,例如 ImHelper.SendMessage(a, b, ‘hello world’) 就能够实现 a -> b 发送音讯。

开源地址:https://github.com/2881099/FreeIM


⛳ 我的项目由来

2017 年进敌人的公司救火,那个时候公司的人员架构、技术架构一团糟,例如通信模块,装备了几人的全职团队负责工作,痛点如下:

1、IM 服务端代码臃肿不堪;

2、逻辑凌乱不堪,IM 服务端代码蕴含了大量业务逻辑,例如聊天记录、订单数据,这原本应该是业务方的数据;

3、凌乱继续放大,IM 服务端为了适应需要,一直减少业务协定,越来越像业务端;

4、沟涌老本巨高,IM 服务端常常和业务方开怼,比方某业务到底以谁的数据为准;

5、通信协定失策,IM 服务端应用原生 Socket 自定义通信协定,起初要保护 WebSocket 及 本人定义协定两套,常常产生音讯无奈传输的问题;

FreeIM 架构的接入之后,遣散了 IM 团队,解决了业务与通信的职责抵触,简化了架构,升高了保护老本。经验 1 年半的生产环境,整顿代码于 2018 年开源。

我是不是太卷了。。别急啊,他们是 java 团队,是不是霎时难受了


⚡ 如何接入?

dotnet add package FreeIM

1、ImServer 服务端

一套永远不须要迭代更新的 IM 服务端,ImServer 反对 .NET6.0、.NETCore2.1+、NETStandard2.0

public void Configure(IApplicationBuilder app)
{
    app.UseFreeImServer(new ImServerOptions
    {Redis = new FreeRedis.RedisClient("127.0.0.1:6379,poolsize=5"),
        Servers = new[] { "127.0.0.1:6001"}, // 集群配置
        Server = "127.0.0.1:6001"
    });
}

2、WebApi 业务端

public void Configure(IApplicationBuilder app)
{
    //...

    ImHelper.Initialization(new ImClientOptions
    {Redis = new FreeRedis.RedisClient("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
HasOnline clientId 判断客户端是否在线
EventBus (上线委托, 离线委托) socket 上线与下线事件
频道 参数 形容
JoinChan (clientId, 频道名) 退出
LeaveChan (clientId, 频道名) 来到
GetChanClientList (频道名) 获取频道所有 clientId
GetChanList 获取所有频道和在线人数
GetChanListByClientId (clientId) 获取用户参加的所有频道
GetChanOnline (频道名) 获取频道的在线人数
SendChanMessage (clientId, 频道名, 音讯内容) 发送音讯,所有在线的用户将收到音讯
  • clientId 应该与用户 id 雷同,或者关联;
  • 频道实用长期的群聊需要,如聊天室、讨论区;

ImHelper 反对 .NetFramework 4.5+、.NetStandard 2.0

3、Html5 终端

终端连贯 websocket 前,应该先申请 WebApi 取得受权过的地址(ImHelper.PrevConnectServer),伪代码:

ajax('/prev-connect-imserver', function(data) {
    var url = data; // 此时的值:ws://127.0.0.1:6001/ws?token=xxxxx
    var sock = new WebSocket(url);
    sock.onmessage = function (e) {//...};
})

📡 我的项目演示

运行环境:.NET6.0 + redis-server 2.8+

cd ImServer && dotnet run –urls=http://*:6001

cd WebApi && dotnet run

关上多个浏览器,别离拜访 http://127.0.0.1:5000 发送群音讯


🎣 剖析痛点

协定痛点:如果浏览器应用 websocket 协定,iOS 应用其余协定,协定不统一将很难保护。

职责痛点:IM 的零碎个别波及【我的好友】、【我的群】、【历史音讯】等等。。

ImServerWebApi(业务方) 该放弃何种关系呢?

用户 A 向好友 B 发送音讯,剖析一下:

  • 须要判断 B 是否为 A 好友;
  • 须要判断 A 是否有权限;

获取历史聊天记录,多个 终端 websocket.send(‘gethistory’),再在 onmessage 定位回调解决,多麻烦啊?

诸如此类业务判断会很简单,应用 ImServer 做业务逻辑,最终 ImServer终端 都将变成巨无霸难以保护。


🌈 设计思路

终端(如浏览器 / 小程序 /iOS/android)对立应用 websocket 连贯 ImServer

ImServer(反对集群)依据 clientId 分区治理 websocket 连贯;

WebApi 应用 ImHelper 调用办法(如:SendMessage、群聊相干办法),将数据推至 Redis chan;

ImServer 订阅 Redis chan,收到音讯后向 终端 推送音讯;

  • 缓解了并发推送音讯过多的问题;
  • 解决了连接数过多的问题;
  • 解耦了业务和通信,架构更加清淅;

    • ImServer 充当音讯转发,连贯保护,代码万年不变、且不须要重启保护
    • WebApi 负责所有业务

举例 1、用户 A 向 B 发送音讯:终端 A ajax -> WebApi -> ImServer -> 终端B websocket.onmessage;

举例 2、获取历史聊天记录:终端 申请 WebApi(业务方) 接口,返回 json(历史音讯)。

举例 3、A 向 B 发文件的例子:

  • A 向 WebApi 传文件
  • WebApi 告诉 ImServer,ImHelper.SendMessage(B, “A 正在给传送文件 …”)
  • B 收到音讯,A 正在给传送文件 …
  • WebApi 文件接管实现时告诉 ImServer,ImHelper.SendMessage(B, “A 文件传输结束(含文件链接)”)
  • B 收到音讯,A 文件传输结束(含文件链接)

FreeIM 强依赖 redis-server 组件性能:

  • 集成了 redis 轻量级的订阅公布性能,实现音讯缓冲发送,前期可更换为其余技术
  • 应用了 redis 存储一些关系数据,如在线 clientId、频道信息、受权信息等

🌳 集群分区

单个 ImServer 实例反对多少个客户端连贯,3 万?如果在线用户有 10 万人,怎么办???

部署 4 个 ImServer

  • ImServer1 订阅 redisChan1
  • ImServer2 订阅 redisChan2
  • ImServer3 订阅 redisChan3
  • ImServer4 订阅 redisChan4

WebApi(业务方) 依据接管方的 clientId 后四位 16 进制与节点总数取模,定位到对应的 redisChan,进行 redis->publish 操作将音讯定位到相应的 ImServer

每个 ImServer 治理着对应的终端连贯,当接管到 redis 订阅音讯后,向对应的终端连贯推送数据。


📰 事件音讯

IM 零碎比拟罕用的有上线、下线,在 ImServer 层能力精确捕获事件,但业务代码不适合在这下面编写了。

此时采纳 redis 公布订阅,将上线、下线等事件向指定频道公布,WebApi(业务方) 通过 ImHelper.EventBus 办法进行订阅捕获。


🌌 有感而发

为什么说 SignalR 不适合做 IM?

1、IM 的特点必然是长连贯,轮训的性能用不上;

2、因为 SignalR 是双工通信的设计,终端 应用 hub.invoke 发送命令给 SignalR 服务端解决业务,适宜用来代替 ajax 缩小 http 申请数量;

3、过多应用 hub,SignalR 服务端会被业务入侵,业务变动频繁后不得不从新公布版本,每次部署所有终端都会断开连接,遇到 5 分钟发一次业务补丁的时候,相似离线和上线提醒好友的性能就无奈实现;

FreeIM 业务和推送拆散设计,终端 连贯永不更新重启 ImServer,业务代码全副在 WebApi 编写,因而重启 WebApi 不会造成连贯断开。


作者是什么人?

作者是一个入行 18 年的老批,他目前写的.net 开源我的项目有:

开源我的项目 形容 开源地址 开源协定
FreeIM 聊天零碎架构 https://github.com/2881099/Fr… MIT
FreeRedis Redis SDK https://github.com/2881099/Fr… MIT
csredis https://github.com/2881099/cs… MIT
FightLandlord 斗 DI 主网络版 https://github.com/2881099/Fi… 学习用处
FreeScheduler 定时工作 https://github.com/2881099/Fr… MIT
IdleBus 闲暇容器 https://github.com/2881099/Id… MIT
FreeSql ORM https://github.com/dotnetcore… MIT
FreeSql.Cloud 分布式 tcc/saga https://github.com/2881099/Fr… MIT
FreeSql.AdminLTE 低代码后盾生成 https://github.com/2881099/Fr… MIT
FreeSql.DynamicProxy 动静代理 https://github.com/2881099/Fr… 学习用处

须要的请拿走,这些都是最近几年的开源作品,以前更早写的就不发了。

退出移动版