基於Netty,徒手擼IM(一):IM系統設計篇

JackJiang發表於2022-07-08

本文收作者“大白菜”分享,有改動。注意:本系列是給IM初學者的文章,IM老油條們還望海涵,勿噴!

1、引言

這又是一篇基於Netty的IM編碼實踐文章,因為合成一篇內容太長,讀起來太累,所以也就順著作者的思路分開成4篇,讀起來心理壓力也就沒那麼大了。

這個系列的幾篇文章分享的是:假設在沒有任何成型的第3方IM庫或SDK的情況下,以網路程式設計的基礎技術視野,思考和實踐如何基於Netty網路庫從零寫一個可以聊天的IM系統的過程,沒有眼花繚亂的架構設計、也沒有高階大氣的模式設計方法論,有的只是從IM入門者的角度的思路和實戰,適合IM初學者閱讀。

本篇主要是徒手擼IM系列的開篇,主要講解的是的IM設計思路,不涉及實踐編碼,希望給你帶來幫助。

學習交流:

  • 移動端IM開發入門文章:《新手入門一篇就夠:從零開發移動端IM》
  • 開源IM框架原始碼:https://github.com/JackJiang2...(備用地址點此)

(本文已同步釋出於:http://www.52im.net/thread-39...

2、知識準備

  • 重要提示:本系列文章主要是程式碼實戰分享,如果你對即時通訊(IM)技術理論瞭解的不多,建議先詳細閱讀:《零基礎IM開發入門:什麼是IM系統?》、《新手入門一篇就夠:從零開發移動端IM》。

不知道 Netty 是什麼?這裡簡單介紹下:

Netty 是一個 Java 開源框架。Netty 提供非同步的、事件驅動的網路應用程式框架和工具,用以快速開發高效能、高可靠性的網路伺服器和客戶端程式。

也就是說,Netty 是一個基於 NIO 的客戶、伺服器端程式設計框架,使用Netty 可以確保你快速和簡單的開發出一個網路應用,例如實現了某種協議的客戶,服務端應用。

Netty 相當簡化和流線化了網路應用的程式設計開發過程,例如,TCP 和 UDP 的 Socket 服務開發。

Netty的基礎入門好文章:

1)新手入門:目前為止最透徹的的Netty高效能原理和框架架構解析
2)寫給初學者:Java高效能NIO框架Netty的學習方法和進階策略
3)史上最通俗Netty框架入門長文:基本介紹、環境搭建、動手實戰

如果你連Java的NIO都不知道是什麼,下面的文章建議優先讀:

1)少囉嗦!一分鐘帶你讀懂Java的NIO和經典IO的區別
2)史上最強Java NIO入門:擔心從入門到放棄的,請讀這篇!
3)Java的BIO和NIO很難懂?用程式碼實踐給你看,再不懂我轉行!

Netty原始碼和API的線上查閱地址:

1)Netty-4.1.x 完整原始碼(線上閱讀版)(* 推薦)
2)Netty-4.1.x API文件(線上版)

3、系列文章

本文是系列文章的第1篇,以下是系列目錄:

《基於Netty,徒手擼IM(一):IM系統設計篇》(* 本文)
《基於Netty,徒手擼IM(二):編碼實踐篇(單聊功能)》
《基於Netty,徒手擼IM(三):編碼實踐篇(群聊功能)》
《基於Netty,徒手擼IM(一):編碼實踐篇(系統優化)》

4、需求分析

業務場景: 本次實戰就是模擬微信的IM聊天,每個客戶端和服務端建立連線,並且可以實現點對點通訊(單聊),點對多點通訊(群聊)。

設計思路: 我們要實現的是點(客戶端)對點(客戶端)的通訊,但是我們大部分情況下接觸的業務都是客戶端和服務端之間的通訊(所謂的C/S模式?),客戶端只需要知道服務端的 IP 地址和埠號即可發起通訊了。那麼客戶端和客戶端應該怎麼去設計呢?

技術思考:難道是手機和手機之間建立通訊連線(所謂的P2P),互相傳送訊息嗎?

這種方案顯然不是很好的方案:

1)首先: 客戶端和客戶端之間通訊,首先需要確定對方的 IP 地址和埠號,顯然不是很現實;
2)其次: 即使有辦法拿到對方的 IP 地址和埠號,那麼每個點(客戶端)既作為服務端還得作為客戶端,無形之中增加了客戶端的壓力。

其實:我們可以使用服務端作為IM聊天訊息的中轉站,由服務端主動往指定客戶端推送訊息。如果是這種模式的話,那麼 Http 協議是無法支援的(因為Http 是無狀態的,只能一請求一響應的模式),於是就只能使用 TCP 協議去實現了。

Jack Jiang注:此處作者表述不太準確,因為雖然HTTP是無狀態的,但一樣可以實現即時通訊能力,有興趣的讀者可以閱讀以下幾篇文章,瞭解一下這些曾經利用HTTP實現即時通訊聊天的技術方法:

《新手入門貼:史上最全Web端即時通訊技術原理詳解》
《Web端即時通訊技術盤點:短輪詢、Comet、Websocket、SSE》
《網頁端IM通訊技術快速入門:短輪詢、長輪詢、SSE、WebSocket》

5、IM單聊思路設計

5.1 通訊架構原理
以下是通訊架構原理圖:

如上圖所示,通訊流程解析如下:

1)實現客戶端和客戶端之間通訊,那麼需要使用服務端作為通訊的中轉站,每個客戶端都必須和服務端建立連線;
2)每個客戶端和服務端建立連線之後,服務端儲存使用者 ID 和通道的對映關係,其中使用者 ID 作為客戶端的唯一標識;
3)客戶端 A 往客戶端 B 傳送訊息時,先把訊息傳送到服務端,再有服務端往客戶端 B 進行推送。

針對上述第“3)”點,服務端如何找到客戶端 B 呢?

客戶端 A 往服務端傳送訊息時,訊息攜帶的資訊有:“客戶端 A 使用者 ID”、“客戶端 B 使用者 ID”、“訊息內容”。這樣服務端就能順利找到服務端 B 的通道並且進行推送訊息了。

5.2 訊息推送流程
每個客戶端和服務端建立連線的時候,必須把個人使用者資訊上傳到服務端,由服務端統一儲存對映關係。如果某個客戶端下線了,則服務端監聽到連線斷開,刪除對應的對映關係。

其次:發起群聊的時候,需要傳遞 touser 欄位,服務端根據該欄位在對映表裡面查詢到對應的連線通道併發起訊息推送。

上述邏輯原理如下圖所示:

5.3 更多的細節
其實在真正要做IM之前,要考慮的技術細節還是很多的,以下這幾篇文章就步及到了典型的幾個IM熱門技術點,有興趣的一定要讀一讀:

《移動端IM開發需要面對的技術問題》
《談談移動端 IM 開發中登入請求的優化》
《IM訊息送達保證機制實現(一):保證線上實時訊息的可靠投遞》
《IM訊息送達保證機制實現(二):保證離線訊息的可靠投遞》
《如何保證IM實時訊息的“時序性”與“一致性”?》

6、IM群聊思路設計

群聊指的是一個組內多個使用者之間的聊天,一個使用者發到群組的訊息會被組內任何一個成員接收 。

具體架構思路如下所示:

如上圖所示,群聊通訊流程解析如下。

1)群聊其實和單聊整體上思路都是一致的,都是需要儲存每個使用者和通道的對應關係,方便後期通過使用者 ID 去查詢到對應的通道,再跟進通道推送訊息。

2)如何把訊息傳送給多個組內的成員呢?

其實很簡單,服務端再儲存另外一份對映關係,那就是聊天室和成員的對映關係。傳送訊息時,首先根據聊天室 ID 找到對應的所有成員,然後再跟進各個成員的 ID 去查詢到對應的通道,最後由每個通道進行訊息的傳送。

3)成員加入某個群聊組的時候,往對映表新增一條記錄,如果成員退群的時候則刪除對應的對映記錄。

通過上面的架構圖可以發現,群聊和單聊相比,其實就是多了一份對映關係而已。

其實群聊是IM裡相對來說技術難度較高的功能,有興趣的讀者可以閱讀下面這幾篇:

《IM單聊和群聊中的線上狀態同步應該用“推”還是“拉”?》
《IM群聊訊息如此複雜,如何保證不丟不重?》
《移動端IM中大規模群訊息的推送如何保證效率、實時性?》
《現代IM系統中聊天訊息的同步和儲存方案探討》
《關於IM即時通訊群聊訊息的亂序問題討論》
《IM群聊訊息的已讀回執功能該怎麼實現?》
《IM群聊訊息究竟是存1份(即擴散讀)還是存多份(即擴散寫)?》
《一套高可用、易伸縮、高併發的IM群聊、單聊架構方案設計實踐》

另外,對於超大規模群聊,技術難度更是指數上升:

《網易雲信技術分享:IM中的萬人群聊技術方案實踐總結》
《阿里釘釘技術分享:企業級IM王者——釘釘在後端架構上的過人之處》
《IM群聊訊息的已讀未讀功能在儲存空間方面的實現思路探討》
《企業微信的IM架構設計揭祕:訊息模型、萬人群、已讀回執、訊息撤回等》
《融雲IM技術分享:萬人群聊訊息投遞方案的思考和實踐》
《微信直播聊天室單房間1500萬線上的訊息架構演進之路》

7、本文小結

本篇主要是幫助讀者掌握單聊和群聊的核心設計思路。

單聊: 主要是伺服器儲存了一份使用者和通道之間的對映關係,傳送訊息的時候,根據接收人 ID 找到其對應的通道 Channel,Channel 的 write () 可以給客戶端傳送訊息。

群聊: 儲存兩份關係,分別是使用者 ID 和 Channel 之間的關係、群組 ID 和使用者 ID 的關係。推送訊息的時候,首先根據聊天組 ID 找到其對應的成員,遍歷每個成員再進行找出其對應的通道即可。

整體來說,思路還是很簡單的,掌握了該設計思路以後,你會發現設計一款 IM 聊天軟體其實也不是很複雜。

8、相關文章

如果你覺得對本系列文章還不夠詳細,可以系統學習以下系列文章:

《跟著原始碼學IM(一):手把手教你用Netty實現心跳機制、斷線重連機制》
《跟著原始碼學IM(二):自已開發IM很難?手把手教你擼一個Andriod版IM》
《跟著原始碼學IM(三):基於Netty,從零開發一個IM服務端》
《跟著原始碼學IM(四):拿起鍵盤就是幹,教你徒手開發一套分散式IM系統》
《跟著原始碼學IM(五):正確理解IM長連線、心跳及重連機制,並動手實現》
《跟著原始碼學IM(六):手把手教你用Go快速搭建高效能、可擴充套件的IM系統》
《跟著原始碼學IM(七):手把手教你用WebSocket打造Web端IM聊天》
《跟著原始碼學IM(八):萬字長文,手把手教你用Netty打造IM聊天》
《跟著原始碼學IM(九):基於Netty實現一套分散式IM系統》
《跟著原始碼學IM(十):基於Netty,搭建高效能IM叢集(含技術思路+原始碼)》
《SpringBoot整合開源IM框架MobileIMSDK,實現即時通訊IM聊天功能》

9、參考資料

[1] 新手入門:目前為止最透徹的的Netty高效能原理和框架架構解析
[2] 理論聯絡實際:一套典型的IM通訊協議設計詳解
[3] 淺談IM系統的架構設計
[4] 簡述移動端IM開發的那些坑:架構設計、通訊協議和客戶端
[5] 一套海量線上使用者的移動端IM架構設計實踐分享(含詳細圖文)
[6] 一套原創分散式即時通訊(IM)系統理論架構方案
[7] 一套高可用、易伸縮、高併發的IM群聊、單聊架構方案設計實踐
[8] 一套億級使用者的IM架構技術乾貨(上篇):整體架構、服務拆分等
[9] 一套億級使用者的IM架構技術乾貨(下篇):可靠性、有序性、弱網優化等
[10] 從新手到專家:如何設計一套億級訊息量的分散式IM系統
[11] 基於實踐:一套百萬訊息量小規模IM系統技術要點總結
[12] 探探的IM長連線技術實踐:技術選型、架構設計、效能優化

(本文已同步釋出於:http://www.52im.net/thread-39...

相關文章