[开源精品] C#.NET 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 的系统一般涉及【我的好友】、【我的群】、【历史消息】等等。。
ImServer
与 WebApi
(业务方) 该保持何种关系呢?
用户A向好友B发送消息,分析一下:
- 需要判断B是否为A好友;
- 需要判断A是否有权限;
获取历史聊天记录,多个 终端
websocket.send('gethistory'),再在 onmessage 定位回调处理,多麻烦啊?
诸如此类业务判断会很复杂,使用 ImServer
做业务逻辑,最终 ImServer
和 终端
都将变成巨无霸难以维护。
设计思路
终端
(如浏览器/小程序/iOS/android) 统一使用 websocket 连接 ImServer
;
ImServer
(支持集群)根据 clientId 分区管理 websocket 连接;
WebApi
使用 ImHelper 调用方法(如:SendMessage、群聊相关方法),将数据推至 Redis channel;
ImServer
订阅 Redis channel,收到消息后向 终端
推送消息;
- 缓解了并发推送消息过多的问题;
- 解决了连接数过多的问题;
- 解耦了业务和通讯,架构更加清淅;
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 订阅 redisChanne1ImServer
2 订阅 redisChanne2ImServer
3 订阅 redisChanne3ImServer
4 订阅 redisChanne4
WebApi
(业务方) 根据接收方的 clientId 后四位 16 进制与节点总数取模,定位到对应的 redisChannel,进行 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/FreeIM | MIT |
FreeRedis | Redis SDK | https://github.com/2881099/FreeRedis | MIT |
csredis | https://github.com/2881099/csredis | MIT | |
FightLandlord | 斗DI主网络版 | https://github.com/2881099/FightLandlord | 学习用途 |
FreeScheduler | 定时任务 | https://github.com/2881099/FreeScheduler | MIT |
IdleBus | 空闲容器 | https://github.com/2881099/IdleBus | MIT |
FreeSql | ORM | https://github.com/dotnetcore/FreeSql | MIT |
FreeSql.Cloud | 分布式tcc/saga | https://github.com/2881099/FreeSql.Cloud | MIT |
FreeSql.AdminLTE | 低代码后台生成 | https://github.com/2881099/FreeSql.AdminLTE | MIT |
FreeSql.DynamicProxy | 动态代理 | https://github.com/2881099/FreeSql.DynamicProxy | 学习用途 |
需要的请拿走,这些都是最近几年的开源作品,以前更早写的就不发了。
[开源精品] C#.NET im 聊天通讯架构设计 -- FreeIM 支持集群、职责分明、高性能的更多相关文章
- .NET Core/.NET5/.NET6 开源项目汇总6:框架与架构设计(DDD、云原生/微服务/容器/DevOps/CICD等)项目
系列目录 [已更新最新开发文章,点击查看详细] 开源项目是众多组织与个人分享的组件或项目,作者付出的心血我们是无法体会的,所以首先大家要心存感激.尊重.请严格遵守每个项目的开源协议后再使用.尊 ...
- Slickflow.NET 开源工作流引擎基础介绍(六)--模块化架构设计和实践
前言:在集成Slickflow.NET 引擎组件过程中,引擎组件需要将用户,角色等资源数据读取进来,供引擎内部调用:而企业客户都是有自己的组织架构模型,在引入模块化架构设计后,引擎组件的集成性更加友好 ...
- MPush开源消息推送系统:简洁、安全、支持集群
引言由于之前自己团队需要一个消息推送系统来替换JPUSH,一直找了很久基本没有真正可用的开源系统所有就直接造了个轮子,造轮子的时候就奔着开源做打算的,只是后来创业项目失败一直没时间整理这一套代码,最近 ...
- [k8s]elk架构设计-k8s集群里搭建
elasticsearch和mysql的对比 https://blog.csdn.net/qq_21383435/article/details/79323383 Mapping ~ Schema m ...
- 瓜子IM智能客服系统的数据架构设计(整理自现场演讲)
本文由ITPub根据封宇在[第十届中国系统架构师大会(SACC2018)]现场演讲内容整理而成. 1.引言 瓜子业务重线下,用户网上看车.预约到店.成交等许多环节都发生在线下.瓜子IM智能客服系统的目 ...
- ABP架构设计交流群-上海线下交流会的内容分享(有高清录像视频的链接)
点这里进入ABP系列文章总目录 ABP架构设计交流群-7月18日上海线下交流会内容分享 因为最近工作特别忙,很久没有更新博客了,真对不起关注我博客和ABP系列文章的朋友! 原计划在7月11日举行的AB ...
- IM即时通讯:如何跳出传统思维来设计聊天室架构?
因为视频直播业务的大规模扩张,聊天室这种功能在最近几年又火了起来.本篇文章将会重点挑选聊天室这个典型场景,和大家分享一下网易云信在实现这个功能时是如何做架构设计的. 相关推荐阅读几十万人同时在线的直播 ...
- [开源 .NET 跨平台 数据采集 爬虫框架: DotnetSpider] [一] 初衷与架构设计
[DotnetSpider 系列目录] 一.初衷与架构设计 二.基本使用 三.配置式爬虫 四.JSON数据解析与配置系统 为什么要造轮子 同学们可以去各大招聘网站查看一下爬虫工程师的要求,大多是招JA ...
- BrnShop开源网上商城第一讲:架构设计
首先在此感谢大家对BrnShop项目的支持和鼓励!我们在发布BrnShop以前曾推测项目会受到不少园友的支持,但没想到园友们的支持大大超过我们的预测.4天6000次浏览,140个推荐,170个评论,8 ...
随机推荐
- 【Redis】集群故障转移
集群故障转移 节点下线 在集群定时任务clusterCron中,会遍历集群中的节点,对每个节点进行检查,判断节点是否下线.与节点下线相关的状态有两个,分别为CLUSTER_NODE_PFAIL和CLU ...
- 如何在vscode 背景配置一个动态小女孩
D:\Microsoft VS Code\resources\app\out\vs\code\electron-browser\workbench <!-- Copyright (C) Micr ...
- WPF开发随笔收录-心电图曲线绘制
一.前言 项目中之前涉及到胎儿心率图曲线的绘制,最近项目中还需要添加心电曲线和血样曲线的绘制功能.今天就来分享一下心电曲线的绘制方式: 二.正文 1.胎儿心率曲线的绘制是通过DrawingVisual ...
- RPA应用场景-财务报表统计整合
场景概述 财务报表统计整合 所涉系统名称 邮储银行系统 人工操作(时间/次) 3小时 所涉人工数量 1 操作频率 每月 场景流程 1.登录各个区支行系统 2.机器人按照要求,自动复选多项业务参数,导出 ...
- 我不就是吃点肉,应该没事吧——爬取一座城市里的烤肉店数据(附完整Python爬虫代码)
写在前面的一点屁话: 对于肉食主义者,吃肉简直幸福感爆棚!特别是烤肉,看着一块块肉慢慢变熟,听着烤盘上"滋滋"的声响,这种期待感是任何其他食物都无法带来的.如果说甜点是" ...
- 015(Power string)(哈希表)
题目:http://ybt.ssoier.cn:8088/problem_show.php?pid=1457 题目思路: 思路就是预设子串的长度,从1开始,而后一段一段试 试到一个对不上的就打回 如果 ...
- 从傅里叶级数(Fourier series)到离散傅里叶变换(Discrete Fourier transform)
从傅里叶级数(Fourier series)到离散傅里叶变换(Discrete Fourier transform) 一. 傅里叶级数(FS) 首先从最直观的开始,我们有一个信号\(x(t)\)(满足 ...
- Ubuntu安装python各版本
编译安装的话,之前遇到过很多小问题,感觉还是通过添加这个ppa方式装的比较稳,缺点是可能安装的比较慢,可配合proxychain4 sudo apt install software-properti ...
- Solution -「基环树」做题记录
写的大多只是思路,比较简单的细节和证明过程就不放了,有需者自取. 基环树简介 简单说一说基环树吧.由名字扩展可得这是一类以环为基础的树(当然显然它不是树. 通常的表现形式是一棵树再加一条非树边,把图画 ...
- 基于ABP实现DDD--仓储实践
由于软件系统中可能有着不同的数据库,不同的ORM,仓储思想的本质是解耦它们.在ABP中具体的实现仓储接口定义在领域层,实现在基础设施层.仓储接口被领域层(比如领域服务)和应用层用来访问数据库,操作 ...