共计 4495 个字符,预计需要花费 12 分钟才能阅读完成。
💻 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 的零碎个别波及【我的好友】、【我的群】、【历史音讯】等等。。
ImServer
与 WebApi
(业务方) 该放弃何种关系呢?
用户 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
:
ImServer
1 订阅 redisChan1ImServer
2 订阅 redisChan2ImServer
3 订阅 redisChan3ImServer
4 订阅 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… | 学习用处 |
须要的请拿走,这些都是最近几年的开源作品,以前更早写的就不发了。