基于Netty,徒手?jǐn)]IM(一):IM系統(tǒng)設(shè)計(jì)篇

本文收作者“大白菜”分享,有改動(dòng)。注意:本系列是給IM初學(xué)者的文章,IM老油條們還望海涵,勿噴!
1、引言
這又是一篇基于Netty的IM編碼實(shí)踐文章,因?yàn)楹铣梢黄獌?nèi)容太長(zhǎng),讀起來(lái)太累,所以也就順著作者的思路分開(kāi)成4篇,讀起來(lái)心理壓力也就沒(méi)那么大了。
這個(gè)系列的幾篇文章分享的是:假設(shè)在沒(méi)有任何成型的第3方IM庫(kù)或SDK的情況下,以網(wǎng)絡(luò)編程的基礎(chǔ)技術(shù)視野,思考和實(shí)踐如何基于Netty網(wǎng)絡(luò)庫(kù)從零寫(xiě)一個(gè)可以聊天的IM系統(tǒng)的過(guò)程,沒(méi)有眼花繚亂的架構(gòu)設(shè)計(jì)、也沒(méi)有高端大氣的模式設(shè)計(jì)方法論,有的只是從IM入門(mén)者的角度的思路和實(shí)戰(zhàn),適合IM初學(xué)者閱讀。
本篇主要是徒手?jǐn)]IM系列的開(kāi)篇,主要講解的是的IM設(shè)計(jì)思路,不涉及實(shí)踐編碼,希望給你帶來(lái)幫助。

學(xué)習(xí)交流:
- 移動(dòng)端IM開(kāi)發(fā)入門(mén)文章:《新手入門(mén)一篇就夠:從零開(kāi)發(fā)移動(dòng)端IM》
- 開(kāi)源IM框架源碼:https://github.com/JackJiang2011/MobileIMSDK(備用地址點(diǎn)此)
(本文已同步發(fā)布于:http://www.52im.net/thread-3963-1-1.html)
2、知識(shí)準(zhǔn)備
* 重要提示:本系列文章主要是代碼實(shí)戰(zhàn)分享,如果你對(duì)即時(shí)通訊(IM)技術(shù)理論了解的不多,建議先詳細(xì)閱讀:《零基礎(chǔ)IM開(kāi)發(fā)入門(mén):什么是IM系統(tǒng)?》、《新手入門(mén)一篇就夠:從零開(kāi)發(fā)移動(dòng)端IM》。
不知道 Netty 是什么?這里簡(jiǎn)單介紹下:
Netty 是一個(gè) Java 開(kāi)源框架。Netty 提供異步的、事件驅(qū)動(dòng)的網(wǎng)絡(luò)應(yīng)用程序框架和工具,用以快速開(kāi)發(fā)高性能、高可靠性的網(wǎng)絡(luò)服務(wù)器和客戶(hù)端程序。
也就是說(shuō),Netty 是一個(gè)基于 NIO 的客戶(hù)、服務(wù)器端編程框架,使用Netty 可以確保你快速和簡(jiǎn)單的開(kāi)發(fā)出一個(gè)網(wǎng)絡(luò)應(yīng)用,例如實(shí)現(xiàn)了某種協(xié)議的客戶(hù),服務(wù)端應(yīng)用。
Netty 相當(dāng)簡(jiǎn)化和流線(xiàn)化了網(wǎng)絡(luò)應(yīng)用的編程開(kāi)發(fā)過(guò)程,例如,TCP 和 UDP 的 Socket 服務(wù)開(kāi)發(fā)。
Netty的基礎(chǔ)入門(mén)好文章:
1)新手入門(mén):目前為止最透徹的的Netty高性能原理和框架架構(gòu)解析
2)寫(xiě)給初學(xué)者:Java高性能NIO框架Netty的學(xué)習(xí)方法和進(jìn)階策略
3)史上最通俗Netty框架入門(mén)長(zhǎng)文:基本介紹、環(huán)境搭建、動(dòng)手實(shí)戰(zhàn)
如果你連Java的NIO都不知道是什么,下面的文章建議優(yōu)先讀:
1)少啰嗦!一分鐘帶你讀懂Java的NIO和經(jīng)典IO的區(qū)別
2)史上最強(qiáng)Java NIO入門(mén):擔(dān)心從入門(mén)到放棄的,請(qǐng)讀這篇!
3)Java的BIO和NIO很難懂?用代碼實(shí)踐給你看,再不懂我轉(zhuǎn)行!
Netty源碼和API的在線(xiàn)查閱地址:
1)Netty-4.1.x 完整源碼(在線(xiàn)閱讀版)(* 推薦)
2)Netty-4.1.x API文檔(在線(xiàn)版)
3、系列文章
本文是系列文章的第1篇,以下是系列目錄:
《基于Netty,徒手?jǐn)]IM(一):IM系統(tǒng)設(shè)計(jì)篇》(* 本文)
《基于Netty,徒手?jǐn)]IM(二):編碼實(shí)踐篇(單聊功能)》
《基于Netty,徒手?jǐn)]IM(三):編碼實(shí)踐篇(群聊功能)》
《基于Netty,徒手?jǐn)]IM(一):編碼實(shí)踐篇(系統(tǒng)優(yōu)化)》
4、需求分析
業(yè)務(wù)場(chǎng)景:?本次實(shí)戰(zhàn)就是模擬微信的IM聊天,每個(gè)客戶(hù)端和服務(wù)端建立連接,并且可以實(shí)現(xiàn)點(diǎn)對(duì)點(diǎn)通信(單聊),點(diǎn)對(duì)多點(diǎn)通信(群聊)。
設(shè)計(jì)思路:?我們要實(shí)現(xiàn)的是點(diǎn)(客戶(hù)端)對(duì)點(diǎn)(客戶(hù)端)的通訊,但是我們大部分情況下接觸的業(yè)務(wù)都是客戶(hù)端和服務(wù)端之間的通訊(所謂的C/S模式?),客戶(hù)端只需要知道服務(wù)端的 IP 地址和端口號(hào)即可發(fā)起通訊了。那么客戶(hù)端和客戶(hù)端應(yīng)該怎么去設(shè)計(jì)呢?
技術(shù)思考:難道是手機(jī)和手機(jī)之間建立通訊連接(所謂的P2P),互相發(fā)送消息嗎?
這種方案顯然不是很好的方案:
1)首先: 客戶(hù)端和客戶(hù)端之間通訊,首先需要確定對(duì)方的 IP 地址和端口號(hào),顯然不是很現(xiàn)實(shí);
2)其次: 即使有辦法拿到對(duì)方的 IP 地址和端口號(hào),那么每個(gè)點(diǎn)(客戶(hù)端)既作為服務(wù)端還得作為客戶(hù)端,無(wú)形之中增加了客戶(hù)端的壓力。
其實(shí):我們可以使用服務(wù)端作為IM聊天消息的中轉(zhuǎn)站,由服務(wù)端主動(dòng)往指定客戶(hù)端推送消息。如果是這種模式的話(huà),那么 Http 協(xié)議是無(wú)法支持的(因?yàn)镠ttp 是無(wú)狀態(tài)的,只能一請(qǐng)求一響應(yīng)的模式),于是就只能使用 TCP 協(xié)議去實(shí)現(xiàn)了。
Jack Jiang注:此處作者表述不太準(zhǔn)確,因?yàn)殡m然HTTP是無(wú)狀態(tài)的,但一樣可以實(shí)現(xiàn)即時(shí)通訊能力,有興趣的讀者可以閱讀以下幾篇文章,了解一下這些曾經(jīng)利用HTTP實(shí)現(xiàn)即時(shí)通訊聊天的技術(shù)方法:
《新手入門(mén)貼:史上最全Web端即時(shí)通訊技術(shù)原理詳解》
《Web端即時(shí)通訊技術(shù)盤(pán)點(diǎn):短輪詢(xún)、Comet、Websocket、SSE》
《網(wǎng)頁(yè)端IM通信技術(shù)快速入門(mén):短輪詢(xún)、長(zhǎng)輪詢(xún)、SSE、WebSocket》
5、IM單聊思路設(shè)計(jì)
5.1 通訊架構(gòu)原理
以下是通訊架構(gòu)原理圖:
?

如上圖所示,通訊流程解析如下:
1)實(shí)現(xiàn)客戶(hù)端和客戶(hù)端之間通訊,那么需要使用服務(wù)端作為通訊的中轉(zhuǎn)站,每個(gè)客戶(hù)端都必須和服務(wù)端建立連接;
2)每個(gè)客戶(hù)端和服務(wù)端建立連接之后,服務(wù)端保存用戶(hù) ID 和通道的映射關(guān)系,其中用戶(hù) ID 作為客戶(hù)端的唯一標(biāo)識(shí);
3)客戶(hù)端 A 往客戶(hù)端 B 發(fā)送消息時(shí),先把消息發(fā)送到服務(wù)端,再有服務(wù)端往客戶(hù)端 B 進(jìn)行推送。
針對(duì)上述第“3)”點(diǎn),服務(wù)端如何找到客戶(hù)端 B 呢?
客戶(hù)端 A 往服務(wù)端發(fā)送消息時(shí),消息攜帶的信息有:“客戶(hù)端 A 用戶(hù) ID”、“客戶(hù)端 B 用戶(hù) ID”、“消息內(nèi)容”。這樣服務(wù)端就能順利找到服務(wù)端 B 的通道并且進(jìn)行推送消息了。
5.2 消息推送流程
每個(gè)客戶(hù)端和服務(wù)端建立連接的時(shí)候,必須把個(gè)人用戶(hù)信息上傳到服務(wù)端,由服務(wù)端統(tǒng)一保存映射關(guān)系。如果某個(gè)客戶(hù)端下線(xiàn)了,則服務(wù)端監(jiān)聽(tīng)到連接斷開(kāi),刪除對(duì)應(yīng)的映射關(guān)系。
其次:發(fā)起群聊的時(shí)候,需要傳遞?touser?字段,服務(wù)端根據(jù)該字段在映射表里面查找到對(duì)應(yīng)的連接通道并發(fā)起消息推送。
上述邏輯原理如下圖所示:

5.3 更多的細(xì)節(jié)
其實(shí)在真正要做IM之前,要考慮的技術(shù)細(xì)節(jié)還是很多的,以下這幾篇文章就步及到了典型的幾個(gè)IM熱門(mén)技術(shù)點(diǎn),有興趣的一定要讀一讀:
《移動(dòng)端IM開(kāi)發(fā)需要面對(duì)的技術(shù)問(wèn)題》
《談?wù)勔苿?dòng)端 IM 開(kāi)發(fā)中登錄請(qǐng)求的優(yōu)化》
《IM消息送達(dá)保證機(jī)制實(shí)現(xiàn)(一):保證在線(xiàn)實(shí)時(shí)消息的可靠投遞》
《IM消息送達(dá)保證機(jī)制實(shí)現(xiàn)(二):保證離線(xiàn)消息的可靠投遞》
《如何保證IM實(shí)時(shí)消息的“時(shí)序性”與“一致性”?》
6、IM群聊思路設(shè)計(jì)
群聊指的是一個(gè)組內(nèi)多個(gè)用戶(hù)之間的聊天,一個(gè)用戶(hù)發(fā)到群組的消息會(huì)被組內(nèi)任何一個(gè)成員接收 。
具體架構(gòu)思路如下所示:

如上圖所示,群聊通訊流程解析如下。
1)群聊其實(shí)和單聊整體上思路都是一致的,都是需要保存每個(gè)用戶(hù)和通道的對(duì)應(yīng)關(guān)系,方便后期通過(guò)用戶(hù) ID 去查找到對(duì)應(yīng)的通道,再跟進(jìn)通道推送消息。
2)如何把消息發(fā)送給多個(gè)組內(nèi)的成員呢?
其實(shí)很簡(jiǎn)單,服務(wù)端再保存另外一份映射關(guān)系,那就是聊天室和成員的映射關(guān)系。發(fā)送消息時(shí),首先根據(jù)聊天室 ID 找到對(duì)應(yīng)的所有成員,然后再跟進(jìn)各個(gè)成員的 ID 去查找到對(duì)應(yīng)的通道,最后由每個(gè)通道進(jìn)行消息的發(fā)送。
3)成員加入某個(gè)群聊組的時(shí)候,往映射表新增一條記錄,如果成員退群的時(shí)候則刪除對(duì)應(yīng)的映射記錄。
通過(guò)上面的架構(gòu)圖可以發(fā)現(xiàn),群聊和單聊相比,其實(shí)就是多了一份映射關(guān)系而已。
其實(shí)群聊是IM里相對(duì)來(lái)說(shuō)技術(shù)難度較高的功能,有興趣的讀者可以閱讀下面這幾篇:
《IM單聊和群聊中的在線(xiàn)狀態(tài)同步應(yīng)該用“推”還是“拉”?》
《IM群聊消息如此復(fù)雜,如何保證不丟不重?》
《移動(dòng)端IM中大規(guī)模群消息的推送如何保證效率、實(shí)時(shí)性?》
《現(xiàn)代IM系統(tǒng)中聊天消息的同步和存儲(chǔ)方案探討》
《關(guān)于IM即時(shí)通訊群聊消息的亂序問(wèn)題討論》
《IM群聊消息的已讀回執(zhí)功能該怎么實(shí)現(xiàn)?》
《IM群聊消息究竟是存1份(即擴(kuò)散讀)還是存多份(即擴(kuò)散寫(xiě))?》
《一套高可用、易伸縮、高并發(fā)的IM群聊、單聊架構(gòu)方案設(shè)計(jì)實(shí)踐》
另外,對(duì)于超大規(guī)模群聊,技術(shù)難度更是指數(shù)上升:
《網(wǎng)易云信技術(shù)分享:IM中的萬(wàn)人群聊技術(shù)方案實(shí)踐總結(jié)》
《阿里釘釘技術(shù)分享:企業(yè)級(jí)IM王者——釘釘在后端架構(gòu)上的過(guò)人之處》
《IM群聊消息的已讀未讀功能在存儲(chǔ)空間方面的實(shí)現(xiàn)思路探討》
《企業(yè)微信的IM架構(gòu)設(shè)計(jì)揭秘:消息模型、萬(wàn)人群、已讀回執(zhí)、消息撤回等》
《融云IM技術(shù)分享:萬(wàn)人群聊消息投遞方案的思考和實(shí)踐》
《微信直播聊天室單房間1500萬(wàn)在線(xiàn)的消息架構(gòu)演進(jìn)之路》
7、本文小結(jié)
本篇主要是幫助讀者掌握單聊和群聊的核心設(shè)計(jì)思路。
單聊:?主要是服務(wù)器保存了一份用戶(hù)和通道之間的映射關(guān)系,發(fā)送消息的時(shí)候,根據(jù)接收人 ID 找到其對(duì)應(yīng)的通道?Channel,Channel 的 write () 可以給客戶(hù)端發(fā)送消息。
群聊:?保存兩份關(guān)系,分別是用戶(hù) ID 和?Channel?之間的關(guān)系、群組 ID 和用戶(hù) ID 的關(guān)系。推送消息的時(shí)候,首先根據(jù)聊天組 ID 找到其對(duì)應(yīng)的成員,遍歷每個(gè)成員再進(jìn)行找出其對(duì)應(yīng)的通道即可。
整體來(lái)說(shuō),思路還是很簡(jiǎn)單的,掌握了該設(shè)計(jì)思路以后,你會(huì)發(fā)現(xiàn)設(shè)計(jì)一款 IM 聊天軟件其實(shí)也不是很復(fù)雜。
8、相關(guān)文章
如果你覺(jué)得對(duì)本系列文章還不夠詳細(xì),可以系統(tǒng)學(xué)習(xí)以下系列文章:
《跟著源碼學(xué)IM(一):手把手教你用Netty實(shí)現(xiàn)心跳機(jī)制、斷線(xiàn)重連機(jī)制》
《跟著源碼學(xué)IM(二):自已開(kāi)發(fā)IM很難?手把手教你擼一個(gè)Andriod版IM》
《跟著源碼學(xué)IM(三):基于Netty,從零開(kāi)發(fā)一個(gè)IM服務(wù)端》
《跟著源碼學(xué)IM(四):拿起鍵盤(pán)就是干,教你徒手開(kāi)發(fā)一套分布式IM系統(tǒng)》
《跟著源碼學(xué)IM(五):正確理解IM長(zhǎng)連接、心跳及重連機(jī)制,并動(dòng)手實(shí)現(xiàn)》
《跟著源碼學(xué)IM(六):手把手教你用Go快速搭建高性能、可擴(kuò)展的IM系統(tǒng)》
《跟著源碼學(xué)IM(七):手把手教你用WebSocket打造Web端IM聊天》
《跟著源碼學(xué)IM(八):萬(wàn)字長(zhǎng)文,手把手教你用Netty打造IM聊天》
《跟著源碼學(xué)IM(九):基于Netty實(shí)現(xiàn)一套分布式IM系統(tǒng)》
《跟著源碼學(xué)IM(十):基于Netty,搭建高性能IM集群(含技術(shù)思路+源碼)》
《SpringBoot集成開(kāi)源IM框架MobileIMSDK,實(shí)現(xiàn)即時(shí)通訊IM聊天功能》
9、參考資料
[1]?新手入門(mén):目前為止最透徹的的Netty高性能原理和框架架構(gòu)解析
[2]?理論聯(lián)系實(shí)際:一套典型的IM通信協(xié)議設(shè)計(jì)詳解
[3]?淺談IM系統(tǒng)的架構(gòu)設(shè)計(jì)
[4]?簡(jiǎn)述移動(dòng)端IM開(kāi)發(fā)的那些坑:架構(gòu)設(shè)計(jì)、通信協(xié)議和客戶(hù)端
[5]?一套海量在線(xiàn)用戶(hù)的移動(dòng)端IM架構(gòu)設(shè)計(jì)實(shí)踐分享(含詳細(xì)圖文)
[6]?一套原創(chuàng)分布式即時(shí)通訊(IM)系統(tǒng)理論架構(gòu)方案
[7]??一套高可用、易伸縮、高并發(fā)的IM群聊、單聊架構(gòu)方案設(shè)計(jì)實(shí)踐
[8]?一套億級(jí)用戶(hù)的IM架構(gòu)技術(shù)干貨(上篇):整體架構(gòu)、服務(wù)拆分等
[9]?一套億級(jí)用戶(hù)的IM架構(gòu)技術(shù)干貨(下篇):可靠性、有序性、弱網(wǎng)優(yōu)化等
[10]?從新手到專(zhuān)家:如何設(shè)計(jì)一套億級(jí)消息量的分布式IM系統(tǒng)
[11]?基于實(shí)踐:一套百萬(wàn)消息量小規(guī)模IM系統(tǒng)技術(shù)要點(diǎn)總結(jié)
[12]?探探的IM長(zhǎng)連接技術(shù)實(shí)踐:技術(shù)選型、架構(gòu)設(shè)計(jì)、性能優(yōu)化
(本文已同步發(fā)布于:http://www.52im.net/thread-3963-1-1.html)