Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/2881099/FreeIM
.NETCore websocket 实现简易、高性能、集群即时通讯组件,支持点对点通讯、群聊通讯、上线下线事件消息等众多实用性功能.
https://github.com/2881099/FreeIM
Last synced: about 2 months ago
JSON representation
.NETCore websocket 实现简易、高性能、集群即时通讯组件,支持点对点通讯、群聊通讯、上线下线事件消息等众多实用性功能.
- Host: GitHub
- URL: https://github.com/2881099/FreeIM
- Owner: 2881099
- License: mit
- Created: 2018-07-09T10:26:54.000Z (over 6 years ago)
- Default Branch: master
- Last Pushed: 2024-04-02T10:13:59.000Z (9 months ago)
- Last Synced: 2024-07-16T08:47:17.008Z (5 months ago)
- Language: C#
- Homepage:
- Size: 78.1 KB
- Stars: 1,424
- Watchers: 54
- Forks: 418
- Open Issues: 37
-
Metadata Files:
- Readme: readme.md
- License: LICENSE
Awesome Lists containing this project
- awesome - 2881099/FreeIM - .NETCore websocket 实现简易、高性能、集群即时通讯组件,支持点对点通讯、群聊通讯、上线下线事件消息等众多实用性功能. (C\#)
README
# FreeIM v2.0.0
- 调整:已将 ClientId Guid 改为 long;
- 修复:JoinChan/LeaveChan 数量统计问题;
- 增加:SendBroadcastMessage 广播消息;
- 优化:SendChanMessage 性能;FreeIM 使用 websocket 协议实现简易、高性能(单机支持5万+连接)、集群即时通讯组件,支持点对点通讯、群聊通讯、上线下线事件消息等众多实用性功能。 `ImCore` 已正式改名为 `FreeIM`。
使用场景:好友聊天、群聊天、直播间、实时评论区、游戏。
*接受定制项目开发,详细请联系作者*
如果对本项目感兴趣,欢迎加入 FreeSql QQ讨论群:8578575
> dotnet add package FreeIM
## ImServer 服务端
```csharp
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"
});
}
//dotnet run --urls=http://127.0.0.1:6001
```
> 一套永远不需要迭代更新的 `ImServer` 服务端,支持 .NET8.0 AOT 发布(C++运行时)。## WebApi 业务端
```csharp
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 | 判断客户端是否在线 |
| ForceOffline | clientId | 强制下线 |
| EventBus | (上线委托, 离线委托) | socket上线与下线事件 || 频道 | 参数 | 描述 |
| - | - | - |
| JoinChan | (clientId, 频道名) | 加入 |
| LeaveChan | (clientId, 频道名) | 离开 |
| GetChanClientList | (频道名) | 获取频道所有clientId |
| GetChanList | - | 获取所有频道和在线人数 |
| GetChanListByClientId | (clientId) | 获取用户参与的所有频道 |
| GetChanOnline | (频道名) | 获取频道的在线人数 |
| SendChanMessage | (clientId, 频道名, 消息内容) | 发送消息,所有在线的用户将收到消息 |
| SendBroadcastMessage | (clientId, 频道名, 消息内容) | 发送广播消息 |- clientId 应该与用户id相同,或者关联;
- 频道适用临时的群聊需求,如聊天室、讨论区;> ImHelper 支持 .NetFramework 4.5+、.NetStandard 2.0
## Html5 终端
终端连接 websocket 前,应该先请求 `WebApi` 获得授权过的地址(ImHelper.PrevConnectServer),伪代码:
```javascript
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 发送群消息
![image](https://user-images.githubusercontent.com/16286519/187127834-d3bb2339-8a9b-4d8c-a0ed-3f1d35b4c7c3.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 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 订阅 redisChan1
- `ImServer`2 订阅 redisChan2
- `ImServer`3 订阅 redisChan3
- `ImServer`4 订阅 redisChan4`WebApi`(业务方) 根据接收方的 clientId 后四位 16 进制与节点总数取模,定位到对应的 redisChan,进行 redis->publish 操作将消息定位到相应的 `ImServer`。
每个 `ImServer` 管理着对应的终端连接,当接收到 redis 订阅消息后,向对应的终端连接推送数据。
## 事件消息
IM 系统比较常用的有上线、下线,在 `ImServer` 层才能准确捕捉事件,但业务代码不合适在这上面编写了。
此时采用 redis 发布订阅,将上线、下线等事件向指定频道发布,`WebApi`(业务方) 通过 ImHelper.EventBus 方法进行订阅捕捉。
![image](https://user-images.githubusercontent.com/16286519/62150466-a46e3980-b330-11e9-86f3-d050160f0913.png)
## 有感而发
为什么说 SignalR 不合适做 IM?
1、IM 的特点必定是长连接,轮训的功能用不上;
2、因为 SignalR 是双工通讯的设计,`终端` 使用 hub.invoke 发送命令给 SignalR 服务端处理业务,适合用来代替 ajax 减少 http 请求数量;
3、过多使用 hub,SignalR 服务端会被业务入侵,业务变化频繁后不得不重新发布版本,每次部署所有终端都会断开连接,遇到5分钟发一次业务补丁的时候,类似离线和上线提示好友的功能就无法实现;
FreeIM 业务和推送分离设计,`终端` 连接永不更新重启 `ImServer` ,业务代码全部在 `WebApi` 编写,因此重启 `WebApi` 不会造成连接断开。
## 💕 Donation (捐赠)
> 感谢你的打赏
- [Alipay](https://www.cnblogs.com/FreeSql/gallery/image/338860.html)
- [WeChat](https://www.cnblogs.com/FreeSql/gallery/image/338859.html)
## 🗄 License (许可证)
[MIT](LICENSE)