天天看點

[開源] .NETCore websocket 即時通訊元件---ImCore

ImCore 利用 webSocket 協定實作簡易、高性能、叢集即時通訊元件,支援點對點通訊、群聊通訊、上線下線事件消息等衆多實用性功能。

Quick Start

dotnet add package ImCore

IM服務端

public void Configure(IApplicationBuilder app)
{
    app.UseimServer(new imServerOptions
    {
        Redis = new CSRedis.CSRedisClient("127.0.0.1:6379,poolsize=5"),
        Servers = new[] { "127.0.0.1:6001" }, //叢集配置
        Server = "127.0.0.1:6001"
    });
}
           
一套永遠不需要疊代更新的IM服務端

WebApi業務端

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

    ImHelper.Initialization(new ImClientOptions
    {
        Redis = new CSRedis.CSRedisClient("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
EventBus (上線委托, 離線委托) socket上線與下線事件
頻道
JoinChan (clientId, 頻道名) 加入
LeaveChan 離開
GetChanClientList (頻道名) 擷取頻道所有clientId
GetChanList 擷取所有頻道和線上人數
GetChanListByClientId (clientId) 擷取使用者參與的所有頻道
GetChanOnline 擷取頻道的線上人數
SendChanMessage (clientId, 頻道名, 消息内容) 發送消息,所有線上的使用者将收到消息
  • clientId 應該與 webApi 的使用者id相同,或者有關聯;
  • 頻道适用臨時的群聊需求,如:聊天室、即時讨論區;

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) {
        //...
    };
})
           

Demo

運作環境:.NETCore 2.1 + redis-server 2.8
下載下傳Redis-x64-2.8.2402.zip,點選 start.bat 運作;
cd imServer && dotnet run
cd web && dotnet run
打開多個浏覽器,通路 http://127.0.0.1:5000 發送群消息
[開源] .NETCore websocket 即時通訊元件---ImCore

設計思路

終端(如浏覽器) 使用 webSocket 連接配接 imServer;

imServer 根據 clientId 分區管理 webSocket 連接配接,可群集部署;

webApi 或其他應用端,使用 ImHelper 調用相關方法(如:SendMessage、群聊相關方法),将資料推至 Redis Channel;

imServer 訂閱 Redis Channel,收到消息後向終端(如浏覽器)推送消息;

1、可緩解并發推送消息過多的問題;

2、可解決連接配接數過多的問題;

3、解決業務和通訊分離,結構更加清淅;

imServer 充當消息轉發,維護連接配接,代碼萬年不變不需要重新開機維護
webApi 負責所有業務

webSocket

如果浏覽器使用 webSocket ,iOS 使用其他協定,協定不一緻的後果很嚴重(難維護)。

建議所有端都使用 webSocket 協定,adorid/ios/h5/小程式 全部支援 webSocket 用戶端。

業務通訊

IM 系統一般涉及【我的好友】、【我的群】、【曆史消息】等等。。

那麼,imServer與業務方(webApi)該保持何種關系呢?

使用者A向好友B發送消息,分析一下:

  • 需要判斷B是否為A好友;
  • 需要判斷A是否有權限;
  • 等等。。

諸如此類業務判斷會很複雜,如果使用imServer做業務協定,它是不是會變成巨無霸難以維護?

又如擷取曆史聊天記錄,難道用戶端要先webSocket.send('gethistory'),再在onmessage裡定位回調處理?

業務和推送分離的設計,即 imServer 隻負責推送工作,webApi 負責業務。

使用者A向B發消息:終端A ajax -> webApi -> imServer -> 終端B webSocket.onmessage;

擷取曆史消息:用戶端請求業務方(webApi)接口,傳回json(曆史消息)。

背後采用 redis 輕量級的訂閱釋出功能,實作消息緩沖發送,方案必備之一,後期可更換為其他技術。比如 webApi 業務發需要通知1000個人,若不用消息緩沖,會對 webApi 應用程式整體将造成性能損耗。

還有使用 redis 存儲一些資料,如線上 clientId,頻道資訊。

叢集分區

單個 imServer 執行個體支援多少個用戶端連接配接,兩千個沒問題?如果線上使用者有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 方法進行訂閱捕捉。

[開源] .NETCore websocket 即時通訊元件---ImCore

A向B發檔案的例子

1、A向 webapi 傳檔案

2、webapi 告訴 imServer,A向B正在傳檔案,ImHelper.SendMessage(B, "A正在給傳送檔案...")

3、B收到消息,A正在傳檔案

4、webapi 檔案接收完成時告訴imServer,A向B檔案傳輸完畢,ImHelper.SendMessage(B, "A檔案傳輸完畢(含檔案連結)")

5、B收到消息,A檔案傳輸完畢(含檔案連結)

有感而發

為什麼說 signalr 不合适做 im?

im 的特點必定是長連接配接,輪訓的功能用不上。

因為他是雙工通訊的設計,用 hub.invoke 發送指令給服務端處理業務,其他就和 ajax 差不多,用來代替 ajax 減少 http 請求數量比較看好。

但是過多使用 hub,signalr 服務端會被業務入侵嚴重,業務變化頻繁後不得不重新釋出版本,每次部署所有終端都會斷開連接配接,遇到5分鐘發一次業務更新檔的時候,類似離線和上線提示好友的功能就無法實作。

ImCore 的設計是業務和推送分離,即 imServer 永不更新重新開機,業務全部在 webApi 上編寫,終端連接配接的是 imServer 就不會頻繁重新開機的問題。

繼續閱讀