前言
ImCore 是一款 .NETCore 下利用 WebSocket 實現的簡易、高性能、集羣即時通訊組件,支持點對點通訊、羣聊通訊、上線下線事件消息等衆多實用性功能。
開源地址:https://github.com/2881099/im ,求 star~~
快速開始
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 | (clientId, 頻道名) | 離開 |
GetChanClientList | (頻道名) | 獲取羣聊頻道所有clientId |
GetChanList | - | 獲取所有羣聊頻道和在線人數 |
GetChanListByClientId | (clientId) | 獲取用戶參與的所有羣聊頻道 |
GetChanOnline | (頻道名) | 獲取羣聊頻道的在線人數 |
SendChanMessage | (clientId, 頻道名, 消息內容) | 發送羣聊消息,所有在線的用戶將收到消息 |
說明:clientId 應該與 webApi的用戶id相同,或者有關聯。
Html5終端
本方案支持集羣分區,前端連接 websocket 前,應該先請求 webApi 獲得地址(ImHelper.PrevConnectServer)。
運行示例
運行環境:.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 發送羣消息
設計思路
imServer 是 websocket 服務中心,可部署多實例,按clientId分區管理socket連接;
webApi 或其他應用端,使用 ImHelper 調用相關方法(如:SendMessage、羣聊相關方法);
消息發送利用了 redis 訂閱發佈技術。每個 imServer 訂閱相應的頻道,收到消息,指派 websocket 向終端(如瀏覽器)發送消息;
1、可緩解併發推送消息過多的問題;
2、可解決連接數過多的問題;
客戶端連接流程:client -> websocket -> imserver
imserver 訂閱消息:client <- imserver <- redis channel
推送消息流程:web1 -> sendmsg方法 -> redis channel -> imserver
imserver 充當消息轉發,及維護連接中心,代碼萬年不變不需要重啓維護;
WebSocket
比較笨的辦法是瀏覽器端使用websocket,其他端socket,這種混亂的設計非常難維護。
強烈建議所有端都使用websocket協議,adorid/ios/h5/小程序全部支持websocket客戶端。
業務與通訊協議
im系統一般涉及【我的好友】、【我的羣】、【歷史消息】等等。。
那麼,imServer與業務方(webApi)該保持何種關係呢?
用戶A向好友B發送消息,分析一下:
- 需要判斷B是否爲A好友;
- 需要判斷A是否有權限;
- 等等。。
諸如此類業務判斷會很複雜,我們試想一下,如果使用imServer做業務協議,它是不是會變成巨無霸難以維護?
又比如獲取歷史聊天記錄,難道客戶端要先websocket.send('gethistory'),再在onmessage裏定位回調處理?
我們可以這樣設定,所有用戶的主動行爲走業務方(webApi),imServer只負責即時消息推送。什麼意思?
用戶A向好友B發送消息:客戶端請求業務方(webApi)接口,由業務方(webApi)後端向imServer發起推送請求,imServer收到指令後,向前端用戶B的websocket發送數據,用戶B收到了消息。
獲取歷史消息:客戶端請求業務方(webApi)接口,返回json(歷史消息)
回執:用戶A如何知道消息發送狀態(成功或失敗或不在線)?imServer端向用戶B發送消息時,把狀態以消息的方式推給用戶A即可(按上面的邏輯),具體請看源碼吧。。。
發送消息
採用 redis 輕量級的訂閱發佈功能,實現消息緩衝發送。
集羣分區
單個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 方法進行訂閱捕捉。
結束語
謝謝支持!