[开源精品] 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
HasOnlineclientId判断客户端是否在线
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=xxxxxvar 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 发送群消息

6a796c4e6135a540888df7d025cf90fc.png


🎣 分析痛点

协议痛点:如果浏览器使用 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

  • ImServer1 订阅 redisChanne1

  • ImServer2 订阅 redisChanne2

  • ImServer3 订阅 redisChanne3

  • ImServer4 订阅 redisChanne4

WebApi(业务方) 根据接收方的 clientId 后四位 16 进制与节点总数取模,定位到对应的 redisChannel,进行 redis->publish 操作将消息定位到相应的 ImServer

每个 ImServer 管理着对应的终端连接,当接收到 redis 订阅消息后,向对应的终端连接推送数据。


📰 事件消息

IM 系统比较常用的有上线、下线,在 ImServer 层才能准确捕捉事件,但业务代码不合适在这上面编写了。

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

3c6780ce28a299671eb662e98aa404c8.png


🌌 有感而发

为什么说 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/FreeIMMIT
FreeRedisRedis SDKhttps://github.com/2881099/FreeRedisMIT
csredis
https://github.com/2881099/csredisMIT
FightLandlord斗DI主网络版https://github.com/2881099/FightLandlord学习用途
FreeScheduler定时任务https://github.com/2881099/FreeSchedulerMIT
IdleBus空闲容器https://github.com/2881099/IdleBusMIT
FreeSqlORMhttps://github.com/dotnetcore/FreeSqlMIT
FreeSql.Cloud分布式tcc/sagahttps://github.com/2881099/FreeSql.CloudMIT
FreeSql.AdminLTE低代码后台生成https://github.com/2881099/FreeSql.AdminLTEMIT
FreeSql.DynamicProxy动态代理https://github.com/2881099/FreeSql.DynamicProxy学习用途

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/283216.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

用websploit获取管理员后台地址

1, use web/dir_scanner 2, set TARGET http://www.****.com 3, run SOURCE: https://sourceforge.net/projects/websploit/ WebSploit Advanced MITM Framework[]Autopwn – Used From Metasploit For Scan and Exploit Target Service[]wmap – Scan,Crawler Target Used Fro…

《ASP.NET Core 6框架揭秘》实例演示[16]:内存缓存与分布式缓存的使用

.NET提供了两个独立的缓存框架,一个是针对本地内存的缓存,另一个是针对分布式存储的缓存。前者可以在不经过序列化的情况下直接将对象存储在应用程序进程的内存中,后者则需要将对象序列化成字节数组并存储到一个独立的“中心数据库”。对于分…

人工智能教程007:创建一个卷积神经网络(2)

2019独角兽企业重金招聘Python工程师标准>>> 我们如何对图像应用卷积 当我们在图像上应用卷积时,我们在两个维度上执行卷积——水平和竖直方向。我们混合两桶信息:第一桶是输入的图像,由三个矩阵构成——RGB三通道,其中…

【系统知识点】linux入门基础命令

大概总结了一下一些基础命令,仅仅是帮助基础使用linux文件系统,如果还有相关基础命令,希望大家留言一起补充汇总一下!命令的基本格式:格式:command [-options] parameter1 parameter2 …ps:第一…

Blazor University (48)依赖注入 —— Scoped 依赖

原文链接:https://blazor-university.com/dependency-injection/dependency-lifetimes-and-scopes/scoped-dependencies/Scoped 依赖Scoped 依赖项类似于 Singleton 依赖项,因为 Blazor 会将相同的实例注入到依赖它的每个对象中,但不同之处在…

c 连接mysql怎么增删改_C++ API方式连接mysql数据库实现增删改查

这里复制的http://www.bitscn.com/pdb/mysql/201407/226252.html一、环境配置1,装好mysql,新建一个C控制台工程(从最简单的弄起,这个会了,可以往任何c工程移植),在vs2010中设置,工程--属性--VC目录--包含目…

阿里云如何实现海量短视频的极速分发?答案在这里!

摘要:短视频行业目前比较火热,但是如何快速的实现海量短视频的极速分发,对于短视频业务提供方来讲是一个比较棘手的问题。阿里云技术专家将带领大家从视频的上传、采集、存储和CDN分发等方面为我们介绍阿里云的整体方案,并且重点讲…

GitHub项目管理维护实用教程

GitHub项目维护教程 1)注册GitHub账户并登陆; 2)在Windows cmd(或Ubuntu中的terminal)中cd到自己的工作目录,将仓库clone下来: 命令: 1 git clone https://github.com/... #项目地址…

图文详解cacti的安装和使用

简介: 1.cacti介绍2.安装服务端3.安装客户端4.添加监控的设备cacti的介绍Cacti是一套基于PHP,MySQL,SNMP及RRDTool开发的网络流量监测图形分析工具Cacti是通过 snmpget来获取数据,使用 RRDtool绘画图形,而且你完全可以不需要了解RRDtool复杂…

记一次 .NET 某金融企业 WPF 程序卡死分析

一:背景 1. 讲故事前段时间遇到了一个难度比较高的 dump,经过几个小时的探索,终于给找出来了,在这里做一下整理,希望对大家有所帮助,对自己也是一个总结,好了,老规矩,上 …

如何将图片放到mysql_怎么将图片添加到mysql中

将图片添加到mysql中的方法:首先将数据库存储图片的字段类型设置为blob二进制大对象类型;然后将图片流转化为二进制;最后将图片插入数据库即可。正常的图片储存要么放进本地磁盘,要么就存进数据库。存入本地很简单,现在…

Java线程与Linux内核线程的映射关系

http://blog.sina.com.cn/s/blog_605f5b4f010198b5.html Linux从内核2.6開始使用NPTL (Native POSIX Thread Library)支持,但这时线程本质上还轻量级进程。Java里的线程是由JVM来管理的。它怎样相应到操作系统的线程是由JVM的实现来确定的。L…

YoursLC 有源 低代码 项目介绍

YoursLC 是我们独立研发的一款低代码产品,YoursLC-yours你们的、LC是低代码low-code的缩写,中文名称:有源低代码, 是一套双输出的低代码产品:既能完整输出功能又能100%输出源码。满足用户高效率、低成本和个性化的需求…

stm32电机控制定时器1_STM32通过PWM控制电机速度

做STM32智能小车的实验中会用到定时器PWM输出,来改变直流电机的转速。分享本文了解如何通过PWM实现对电机速度的控制。PWM控制电机速度的基本原理PWM(Pulse Width Modulation),也就是脉冲宽度调制。PWM中有一个比较重要的概念,占空比&#xf…

走向无后端的系统开发实践:CRUD自动化与强约定的REST接口

2019独角兽企业重金招聘Python工程师标准>>> ttp://mp.weixin.qq.com/s?__bizMzAwMDU1MTE1OQ&idx1&mid2653548079&sn2377b625db58b2ea93c3ef2d87e4c395 转载于:https://my.oschina.net/yunjie/blog/806130

mysql char varchar 性能_Mysql小细节:varchar与char在性能上的特点

varchar与char的一个主要区别是存储方式的不同varchar 是变长存储占用的存储空间 存储内容实际大小 长度记录位char 是定长存储占用的存储空间 字段声明的宽度存储方式对性能是有影响的例如分别使用 varchar(10) 与 varchar(255) 定义一个字段,实际存储的字符串为…

Dubbo源码解析之Zookeeper连接

2019独角兽企业重金招聘Python工程师标准>>> 注:Dubbo的版本是2.5.7。 图1 RegistryProtocol的export时序图 注册中心有Zookeeper、Redis、Dubbo,分别对应ZookeeperRegistry、RedisRegistry、MulticastRegistry。 连接Dubbo的客户端有俩种&am…

SHELL 脚本小技巧

脚本很简单,直接上功能介绍及脚本,可以做模板使用: 记录日志,记录脚本开始执行时间、结束时间usage 函数,脚本需接参数执行,避免误执行,告诉用户,这个脚本的使用方法加锁,创建锁文件…

WinForm(十)项目框架结构

看到下面的项目结构,是否曾经相识?不要笑,这也是一种项目结构,极简主义。项目结构没有对错,合适就好,但也要有几个要求,至少要做到结构明确,清晰,当然上图的结构清晰&…

mysql索引有哪些了解_Mysql索引(简单了解)

Mysql的存储引擎,可以针对不同的表使用不同的存储引擎MyISAM:插入,查血速度快,但是不支持事物,所以适用于数据仓库,Web等InnoDB:支持事物,所以适合于事物型数据库Memory:…