Jack Jiang

          我的最新工程MobileIMSDK:http://git.oschina.net/jackjiang/MobileIMSDK
          posts - 499, comments - 13, trackbacks - 0, articles - 1

               摘要: 本文原題“百度直播消息服務架構實踐”,由百度APP消息中臺團隊原創(chuàng)分享于“百度Geek說”公眾號,為了讓文章內容更通俗易懂,本次已做排版優(yōu)化和內容重新劃分,原文鏈接在文末。1、引言一套完整的直播系統(tǒng)核心功能有兩個:1)實時音視頻的推拉流;2)直播間消息流的收發(fā)(包括聊天消息、彈幕、指令等)。本文主要分享的是百度直播的消息系統(tǒng)的架構設計實踐和演進過程。...  閱讀全文

          posted @ 2021-04-27 15:17 Jack Jiang 閱讀(210) | 評論 (0)編輯 收藏

               摘要: 文中引用了參考資料中的部分內容,本文參考資料詳見文末“參考資料”一節(jié),感謝資料分享者。1、引言對于IM開發(fā)者而言,網(wǎng)絡保活這件事再熟悉不過了,比如這是我最近一篇有關網(wǎng)絡保活話題文章《一文讀懂即時通訊應用中的網(wǎng)絡心跳包機制:作用、原理、實現(xiàn)思路等》,以及我分享的大量代碼實戰(zhàn)編碼中也都必須要考慮這個問題的實現(xiàn),比如最近的這篇《跟著源碼學IM(五):正確理解IM長連接、心跳及重連...  閱讀全文

          posted @ 2021-04-19 15:10 Jack Jiang 閱讀(290) | 評論 (0)編輯 收藏

               摘要: 本文作者芋艿,原題“使用 Netty 實現(xiàn) IM 聊天賊簡單”,本底價有修訂和改動。一、本文引言上篇《跟著源碼學IM(七):手把手教你用WebSocket打造Web端IM聊天》中,我們使用 WebSocket 實現(xiàn)了一個簡單的 IM 功能,支持身份認證、私聊消息、群聊消息。然后就有人發(fā)私信,希望使用純 Netty 實現(xiàn)一個類似的功能,因此就有了本文。注:源碼請從同步鏈接附件...  閱讀全文

          posted @ 2021-04-12 15:43 Jack Jiang 閱讀(323) | 評論 (0)編輯 收藏

               摘要: 本文作者芋艿,原題“芋道 Spring Boot WebSocket 入門”,本次有修訂和改動。一、引言WebSocket如今在Web端即時通訊技術應用里使用廣泛,不僅用于傳統(tǒng)PC端的網(wǎng)頁里,也被很多移動端開發(fā)者用于基于HTML5的混合APP里。對于想要在基于Web的應用里添加IM、推送等實時通信功能,WebSocket幾乎是必須要掌握的技術。本文將基于Tomcat和Spr...  閱讀全文

          posted @ 2021-04-06 22:05 Jack Jiang 閱讀(225) | 評論 (0)編輯 收藏

               摘要: 本文原作者Chank,原題“如何設計一個億級消息量的 IM 系統(tǒng)”,為了提升內容質量,本次有修訂和改動。1、寫有前面本文將在億級消息量、分布式IM系統(tǒng)這個技術前提下,分析和總結實現(xiàn)這套系統(tǒng)所需要掌握的知識點,內容沒有高深的技術概念,盡量做到新手老手皆能讀懂。本文不會給出一套通用的IM方案,也不會評判某種架構的好壞,而是討論設計IM系統(tǒng)的常見難題跟業(yè)界的解決方案。因為也沒有所...  閱讀全文

          posted @ 2021-03-29 22:36 Jack Jiang 閱讀(258) | 評論 (0)編輯 收藏

          本文內容和編寫思路是基于鄧昀澤的“大規(guī)模并發(fā)IM服務架構設計”、“IM的弱網(wǎng)場景優(yōu)化”兩文的提綱進行的,感謝鄧昀澤的無私分享。

          1、引言

          接上篇《一套億級用戶的IM架構技術干貨(上篇):整體架構、服務拆分等》,本文主要聚焦這套億級用戶的IM架構的一些比較細節(jié)但很重要的熱門問題上,比如:消息可靠性、消息有序性、數(shù)據(jù)安全性、移動端弱網(wǎng)問題等。

          以上這些熱門IM問題每個話題其實都可以單獨成文,但限于文章篇幅,本文不會逐個問題詳細深入地探討,主要以拋磚引玉的方式引導閱讀者理解問題的關鍵,并針對問題提供專項研究文章鏈接,方便有選擇性的深入學習。希望本文能給你的IM開發(fā)帶來一些益處。

          本文已同步發(fā)布于“即時通訊技術圈”公眾號,歡迎關注。公眾號上的鏈接是:點此進入

          2、系列文章

          為了更好以進行內容呈現(xiàn),本文拆分兩了上下兩篇。

          本文是2篇文章中的第2篇:

          一套億級用戶的IM架構技術干貨(上篇):整體架構、服務拆分等

          一套億級用戶的IM架構技術干貨(下篇):可靠性、有序性、弱網(wǎng)優(yōu)化等》(本文)

          本篇主要聚焦這套億級用戶的IM架構的一些比較細節(jié)但很重要的熱門問題上。

          3、消息可靠性問題

          消息的可靠性是IM系統(tǒng)的典型技術指標,對于用戶來說,消息能不能被可靠送達(不丟消息),是使用這套IM的信任前提。

          換句話說,如果這套IM系統(tǒng)不能保證不丟消息,那相當于發(fā)送的每一條消息都有被丟失的概率,對于用戶而言,一定會不會“放心”地使用它,即“不信任”這套IM。

          從產(chǎn)品經(jīng)理的角度來說,有這樣的技術障礙存在,再怎么費力的推廣,最終用戶都會很快流失。所以一套IM如果不能保證消息的可靠性,那問題是很嚴重的。

          PS:如果你對IM消息可靠性的問題還沒有一個直觀的映象的話,通過《零基礎IM開發(fā)入門(三):什么是IM系統(tǒng)的可靠性?》這篇文章可以通俗易懂的理解它。

          如上圖所示,消息可靠性主要依賴2個邏輯來保障:

          • 1)上行消息可靠性;
          • 2)下行消息可靠性。

          1)針對上行消息的可靠性,可以這樣的思路來處理:

          用戶發(fā)送一個消息(假設協(xié)議叫PIMSendReq),用戶要給這個消息設定一個本地ID,然后等待服務器操作完成給發(fā)送者一個PIMSendAck(本地ID一致),告訴用戶發(fā)送成功了。

          如果等待一段時間,沒收到這個ACK,說明用戶發(fā)送不成功,客戶端SDK要做重試操作。

          2)針對下行消息的可靠性,可以這樣的思路來處理:

          服務收到了用戶A的消息,要把這個消息推送給B、C、D 3個人。假設B臨時掉線了,那么在線推送很可能會失敗。

          因此確保下行可靠性的核心是:在做推送前要把這個推送請求緩存起來。

          這個緩存由存儲系統(tǒng)來保證,MsgWriter要維護一個(離線消息列表),用戶的一條消息,要同時寫入B、C、D的離線消息列表,B、C、D收到這個消息以后,要給存儲系統(tǒng)一個ACK,然后存儲系統(tǒng)把消息ID從離線消息列表里拿掉。

          針對消息的可靠性問題,具體的解決思路還可以從另一個維度來考慮:即實時消息的可靠性和離線消息的可靠性。

          有興趣可以深入讀一讀這兩篇:

          IM消息送達保證機制實現(xiàn)(一):保證在線實時消息的可靠投遞

          IM消息送達保證機制實現(xiàn)(二):保證離線消息的可靠投遞

          而對于離線消息的可靠性來說,單聊和群聊又有很大區(qū)別,有關群聊的離線消息可靠投遞問題,可以深入讀一讀《IM開發(fā)干貨分享:如何優(yōu)雅的實現(xiàn)大量離線消息的可靠投遞》。

          4、消息有序性問題

          消息的有序性問題是分布式IM系統(tǒng)中的另一個技術“硬骨頭”。

          因為是分布式系統(tǒng),客戶端和服務器的時鐘可能是不同步的。如果簡單依賴某一方的時鐘,就會出現(xiàn)大量的消息亂序。

          比如只依賴客戶端的時鐘,A比B時間晚30分鐘。所有A給B發(fā)消息,然后B給A回復。

          發(fā)送順序是:

          客戶端A:“XXX”

          客戶端B:“YYY”

          接收方的排序就會變成:

          客戶端B:“YYY”

          客戶端A:“XXX”

          因為A的時間晚30分鐘,所有A的消息都會排在后面。

          如果只依賴服務器的時鐘,也會出現(xiàn)類似的問題,因為2個服務器時間可能也不一致。雖然客戶端A和客戶端B時鐘一致,但是A的消息由服務器S1處理,B的消息由服務器S2處理,也會導致同樣消息亂序。

          為了解決這種問題,我的思路是通過可以做這樣一系列的操作來實現(xiàn)。

          1)服務器時間對齊:

          這部分就是后端運維的鍋了,由系統(tǒng)管理員來盡量保障,沒有別的招兒。

          2)客戶端通過時間調校對齊服務器時間:

          比如:客戶端登錄以后,拿客戶端時間和服務器時間做差值計算,發(fā)送消息的時候考慮這部分差值。

          在我的im架構里,這個能把時間對齊到100ms這個級,差值再小的話就很困難了,因為協(xié)議在客戶端和服務器之間傳遞速度RTT也是不穩(wěn)定的(網(wǎng)絡傳輸存在不可控的延遲風險嘛)。

          3)消息同時帶上本地時間和服務器時間:

          具體可以這樣的處理:排序的時候,對于同一個人的消息,按照消息本地時間來排;對于不同人的消息,按照服務器時間來排,這是插值排序算法。

          PS:關于消息有序性的問題,顯然也不是上面這三兩句話能講的清楚,如果你想更通俗一理解它,可以讀一讀《零基礎IM開發(fā)入門(四):什么是IM系統(tǒng)的消息時序一致性?》。

          另外:從技術實踐可行性的角度來說,《一個低成本確保IM消息時序的方法探討》、《如何保證IM實時消息的“時序性”與“一致性”?》這兩篇中的思路可以借鑒一下。

          實際上,消息的排序問題,還可以從消息ID的角度去處理(也就是通過算法讓消息ID產(chǎn)生順序性,從而根據(jù)消息ID就能達到消息排序的目的)。

          有關順序的消息ID算法問題,這兩篇非常值得借鑒:IM消息ID技術專題(一):微信的海量IM聊天消息序列號生成實踐(算法原理篇)》、《IM消息ID技術專題(三):解密融云IM產(chǎn)品的聊天消息ID生成策略》,我就不廢話了。

          5、消息已讀同步問題

          消息的已讀未讀功能,如下圖所示: 

          上圖就是釘釘中的已讀未讀消息。這在企業(yè)IM場景下非常有用(因為做領導的都喜歡,你懂的)。

          已讀未讀功能,對于一對一的單聊消息來說,還比較好理解:就是多加一條對應的回執(zhí)息(當用戶閱讀這條消息時發(fā)回)。

          但對于群聊這說,這一條消息有多少人已讀、多少人未讀,想實現(xiàn)這個效果,那就真的有點麻煩了。對于群聊的已讀未讀功能實現(xiàn)邏輯,這里就不展開了,有興趣可以讀一下這篇《IM群聊消息的已讀回執(zhí)功能該怎么實現(xiàn)?》。

          回歸到本節(jié)的主題“已讀同步”的問題,這顯示難度又進一級,因為已讀未讀回執(zhí)不只是針對“賬號”,現(xiàn)在還要細分到“同一賬號在不同端登陸”的情況,對于已讀回執(zhí)的同步邏輯來說,這就有點復雜化了。

          在這里,根據(jù)我這邊IM架構的實踐經(jīng)驗,提供一些思路。

          具體來說就是:用戶可能有多個設備登錄同一個賬戶(比如:Web PC和移動端同時登陸),這種情況下的已讀未讀功能,就需要來實現(xiàn)已讀同步,否則在設備1看過的消息,設備2看到依然是未讀消息,從產(chǎn)品的角度來說,這就影響用戶體驗了。

          對于我的im架構來說,已讀同步主要依賴2個邏輯來保證:

          • 1)同步狀態(tài)維護,為用戶的每一個Session,維護一個時間戳,保存最后的讀消息時間;
          • 2)如果用戶打開了某個Session,且用戶有多個設備在線,發(fā)送一條PIMSyncRead消息,通知其它設備。

          6、數(shù)據(jù)安全問題

          6.1 基礎

          IM系統(tǒng)架構中的數(shù)據(jù)安全比一般系統(tǒng)要復雜一些,從通信的角度來說,它涉及到socket長連接通信的安全性和http短連接的兩重安全性。而隨著IM在移動端的流行,又要在安全性、性能、數(shù)據(jù)流量、用戶體驗這幾個維度上做權衡,所以想要實現(xiàn)一套完善的IM安全架構,要面臨的挑戰(zhàn)是很多的。

          IM系統(tǒng)架構中,所謂的數(shù)據(jù)安全,主要是通信安全和內容安全。

          6.2 通信安全

          所謂的通信安全,這就要理解IM通信的服務組成。

          目前來說,一個典型的im系統(tǒng),主要由兩種通信服務組成:

          • 1)socket長連接服務:技術上也就是多數(shù)人耳熟能詳?shù)木W(wǎng)絡通信這一塊,再細化一點也就是tcp、udp協(xié)議這一塊;
          • 2)http短連接服務:也就是最常用的http rest接口那些。

          對于提升長連接的安全性思路,可以深入閱讀《通俗易懂:一篇掌握即時通訊的消息傳輸安全原理》。另外,微信團隊分享的《微信新一代通信安全解決方案:基于TLS1.3的MMTLS詳解》一文,也非常有參考意義。

          如果是通信安全級別更高的場景,可以參考《即時通訊安全篇(二):探討組合加密算法在IM中的應用》,文中關于組合加密算法的使用思路非常不錯。

          至于短連接安全性,大家就很熟悉了,開啟https多數(shù)情況下就夠用了。如果對于https不甚了解,可以從這幾篇開始:《一文讀懂Https的安全性原理、數(shù)字證書、單項認證、雙項認證等》、《即時通訊安全篇(七):如果這樣來理解HTTPS,一篇就夠了》。

          6.3 內容安全

          這個可能不太好理解,上面既然實現(xiàn)了通信安全,那為什么還要糾結“內容安全”?

          我們了解一下所謂的密碼學三大作用:加密( Encryption)、認證(Authentication),鑒定(Identification) 。

          詳細來說就是:

          加密:防止壞人獲取你的數(shù)據(jù)。

          認證:防止壞人修改了你的數(shù)據(jù)而你卻并沒有發(fā)現(xiàn)。

          鑒權:防止壞人假冒你的身份。

          在上節(jié)中,惡意攻擊者如果在通信環(huán)節(jié)繞開或突破了“鑒權”、“認證”,那么依賴于“鑒權”、“認證”的“加密”,實際上也有可有被破解。

          針對上述問題,那么我們需要對內容進行更加安全獨立的加密處理,就這是所謂的“端到端加密”(E2E)。

          比如,那個號稱無法被破解的IM——Telegram,實際上就是使用了端到端加密技術。

          關于端到端加密,這里就不深入探討,這里有兩篇文章有興趣地可以深入閱讀:

          移動端安全通信的利器——端到端加密(E2EE)技術詳解

          簡述實時音視頻聊天中端到端加密(E2EE)的工作原理

          7、雪崩效應問題

          在分布式的IM架構中,存在雪崩效應問題。

          我們知道,分布式的IM架構中,為了高可用性,用戶每次登陸都是根據(jù)負載均衡算法分配到不同的服務器。那么問題就來了。

          舉個例子:假設有5個機房,其中A機房故障,導致這個機房先前服務的用戶都跑去B機房。B機房不堪重負也崩潰了,A+B的用戶跑去機房C,連鎖反應會導致所有服務掛掉。

          防止雪崩效應需要在服務器架構,客戶端鏈接策略上有一些配合的解決方案。服務器需要有限流能力作為基礎,主要是限制總服務用戶數(shù)和短時間鏈接用戶數(shù)。

          在客戶端層面,發(fā)現(xiàn)服務斷開之后要有一個策略,防止大量用戶同一時間去鏈接某個服務器。

          通常有2種方案:

          • 1)退避:重連之間設置一個隨機的間隔;
          • 2)LBS:跟服務器申請重連的新的服務器IP,然后由LBS服務去降低短時間分配到同一個服務器的用戶量。

          這2種方案互不沖突,可以同時做。

          8、弱網(wǎng)問題

          8.1 弱網(wǎng)問題的原因

          鑒于如今IM在移動端的流行,弱網(wǎng)是很常態(tài)的問題。電梯、火車上、開車、地鐵等等場景,都會遇到明顯的弱網(wǎng)問題。

          那么為什么會出現(xiàn)弱網(wǎng)問題?

          要回答這個問題,那就需要從無線通信的原理上去尋找答案。

          因為無線通信的質量受制于很多方面的因素,比如:無線信號強弱變化快、信號干擾、通信基站分布不均、移動速度太快等等。要說清楚這個問題,那就真是三天三夜都講不完。

          有興趣的讀者,一定要仔細閱讀下面這幾篇文章,類似的跨學科科譜式文章并不多見:

          IM開發(fā)者的零基礎通信技術入門(十一):為什么WiFi信號差?一文即懂!

          IM開發(fā)者的零基礎通信技術入門(十二):上網(wǎng)卡頓?網(wǎng)絡掉線?一文即懂!

          IM開發(fā)者的零基礎通信技術入門(十三):為什么手機信號差?一文即懂!

          IM開發(fā)者的零基礎通信技術入門(十四):高鐵上無線上網(wǎng)有多難?一文即懂!

          弱網(wǎng)問題是移動端APP的必修課,下面這幾篇總結也值得借鑒:

          移動端IM開發(fā)者必讀(一):通俗易懂,理解移動網(wǎng)絡的“弱”和“慢”

          移動端IM開發(fā)者必讀(二):史上最全移動弱網(wǎng)絡優(yōu)化方法總結

          現(xiàn)代移動端網(wǎng)絡短連接的優(yōu)化手段總結:請求速度、弱網(wǎng)適應、安全保障

          百度APP移動端網(wǎng)絡深度優(yōu)化實踐分享(三):移動端弱網(wǎng)優(yōu)化篇

          8.2 IM針對弱網(wǎng)問題的處理

          對于IM來說,弱網(wǎng)問題并不是很復雜,核心是做好消息的重發(fā)、排序以及接收端的重試。

          為了解決好弱網(wǎng)引發(fā)的IM問題,通常可以通過以下手段改善:

          • 1)消息自動重發(fā);
          • 2)離線消息接收;
          • 3)重發(fā)消息排序;
          • 4)離線指令處理。

          下面將逐一展開討論。

          8.3 消息自動重發(fā)

          坐地鐵的時候,經(jīng)常遇到列車開起來以后,網(wǎng)絡斷開,發(fā)送消息失敗。

          這時候產(chǎn)品有2種表現(xiàn)形式:

          • a、直接告訴用戶發(fā)送失敗;
          • b、保持發(fā)送狀態(tài),自動重試3-5次(3分鐘)以后告訴用戶發(fā)送失敗。

          顯然:自動重試失敗以后再告訴用戶發(fā)送失敗體驗要好很多。尤其是在網(wǎng)絡閃斷情況下,重試成功率很高,很可能用戶根本感知不到有發(fā)送失敗。

          從技術上:客戶端IMSDK要把每條消息的狀態(tài)監(jiān)控起來。發(fā)送消息不能簡單的調用一下網(wǎng)絡發(fā)送請求,而是要有一個狀態(tài)機,管理幾個狀態(tài):初始狀態(tài),發(fā)送中,發(fā)送失敗,發(fā)送超時。對于失敗和超時的狀態(tài),要啟用重試機制。

          這里還有一篇關于重試機制設計的討論帖子,有興趣可以看看:完全自已開發(fā)的IM該如何設計“失敗重試”機制?》。

          IM消息送達保證機制實現(xiàn)(一):保證在線實時消息的可靠投遞》一文中關于消息超時與重傳機制的實現(xiàn)思路,也可以參考一下。

          8.4 離線消息接收

          現(xiàn)代IM是沒有“在線”這個狀態(tài)的,不需要給用戶這個信息。但是從技術的層面,用戶掉線了還是要正確的去感知的。

          感知方法有幾條:

          • a、信令長連接狀態(tài):如果長時間沒收到到服務器的心跳反饋,說明掉線了;
          • b、網(wǎng)絡請求失敗次數(shù):如果多次網(wǎng)絡請求失敗,說明”可能“掉線了;
          • c、設備網(wǎng)絡狀態(tài)檢測:直接檢測網(wǎng)卡狀態(tài)就好,一般Android/iOS/Windows/Mac都有相應系統(tǒng)API。

          正確檢測到網(wǎng)絡狀態(tài)以后,發(fā)現(xiàn)網(wǎng)絡從”斷開到恢復“的切換,要去主動拉取離線階段的消息,就可以做到弱網(wǎng)狀態(tài)不丟消息(從服務器的離線消息列表拉取)。

          上面文字中提到的網(wǎng)絡狀態(tài)的確定,涉及到IM里網(wǎng)絡連接檢查和保活機制問題,是IM里比較頭疼的問題。

          一不小心,又踩進了IM網(wǎng)絡保活這個坑,我就不在這里展開,有興趣一定要讀讀下面的文章:

          為何基于TCP協(xié)議的移動端IM仍然需要心跳保活機制?

          一文讀懂即時通訊應用中的網(wǎng)絡心跳包機制:作用、原理、實現(xiàn)思路等

          微信團隊原創(chuàng)分享:Android版微信后臺保活實戰(zhàn)分享(網(wǎng)絡保活篇)

          移動端IM實踐:實現(xiàn)Android版微信的智能心跳機制

          移動端IM實踐:WhatsApp、Line、微信的心跳策略分析

          8.5 重發(fā)消息排序

          弱網(wǎng)邏輯的另一個坑是消息排序。

          假如有A、B、C  3條消息,A、C發(fā)送成功,B發(fā)送的時候遇到了網(wǎng)絡閃斷,B觸發(fā)自動重試。

          那么接收方的接收順序應該是 A B C還是A C B呢?我觀察過不同的IM產(chǎn)品,處理邏輯各不相同,這個大家有興趣可以去玩一下。

          這個解決方法是要依賴上一篇服務架構里提到的差值排序,同一個人發(fā)出的消息,排序按消息附帶的本地時間來排。不同人的消息,按照服務器時間排序。

          具體我這邊就不再得復,可以回頭看看本篇中的第四節(jié)“4、消息有序性問題”。

          8.6 離線指令處理

          部分指令操作的時候,網(wǎng)絡可能出現(xiàn)了問題,等網(wǎng)絡恢復以后,要自動同步給服務器。

          舉一個例子,大家可以試試手機設置為飛行模式,然后在微信里刪除一個聯(lián)系人,看看能不能刪除。然后重新打開網(wǎng)絡,看看這個數(shù)據(jù)會不會同步到服務器。

          類似的邏輯也適用于已讀同步等場景,離線狀態(tài)看過的信息,要正確的跟服務器同步。

          8.7 小結一下

          IM的弱網(wǎng)處理,其實相對還是比較簡單的,基本上自動重試+消息狀態(tài)就可以解決絕大部分的問題了。

          一些細節(jié)處理也并不復雜,主要原因是IM的消息量比較小,網(wǎng)絡恢復后能快速的恢復操作。

          視頻會議在弱網(wǎng)下的邏輯,就要復雜的多了。尤其是高丟包的弱網(wǎng)環(huán)境下,要盡力去保證音視頻的流暢性。

          9、本文小結

          《一套億級用戶的IM架構技術干貨》這期文章的上下兩篇就這么侃完了,上篇涉及到的IM架構問題倒還好,下篇一不小心又帶出了IM里的各種熱門問題“坑”,搞IM開發(fā)直是一言難盡。。。

          建議IM開發(fā)的入門朋友們,如果想要系統(tǒng)地學習移動端IM開發(fā)的話,應該去讀一讀我整理的那篇IM開發(fā)“從入門到放棄”的文章(哈哈哈),就是這篇《新手入門一篇就夠:從零開發(fā)移動端IM》。具體我就不再展開了,不然這篇幅又要剎不住車了。。。

          10、參考資料

          [1] 大規(guī)模并發(fā)IM服務架構設計

          [2] IM的弱網(wǎng)場景優(yōu)化

          [3] 零基礎IM開發(fā)入門(三):什么是IM系統(tǒng)的可靠性?

          [4] IM消息送達保證機制實現(xiàn)(一):保證在線實時消息的可靠投遞

          [5] IM開發(fā)干貨分享:如何優(yōu)雅的實現(xiàn)大量離線消息的可靠投遞

          [6] 即時通訊安全篇(二):探討組合加密算法在IM中的應用

          [7] 微信新一代通信安全解決方案:基于TLS1.3的MMTLS詳解

          附錄:更多IM開發(fā)文章匯總

          零基礎IM開發(fā)入門(一):什么是IM系統(tǒng)?

          零基礎IM開發(fā)入門(二):什么是IM系統(tǒng)的實時性?

          IM開發(fā)干貨分享:如何優(yōu)雅的實現(xiàn)大量離線消息的可靠投遞

          IM開發(fā)干貨分享:有贊移動端IM的組件化SDK架構設計實踐

          IM開發(fā)寶典:史上最全,微信各種功能參數(shù)和邏輯規(guī)則資料匯總

          IM開發(fā)干貨分享:我是如何解決大量離線消息導致客戶端卡頓的

          從客戶端的角度來談談移動端IM的消息可靠性和送達機制

          騰訊技術分享:社交網(wǎng)絡圖片的帶寬壓縮技術演進之路

          移動端IM中大規(guī)模群消息的推送如何保證效率、實時性?

          移動端IM開發(fā)需要面對的技術問題

          開發(fā)IM是自己設計協(xié)議用字節(jié)流好還是字符流好?

          請問有人知道語音留言聊天的主流實現(xiàn)方式嗎?

          IM單聊和群聊中的在線狀態(tài)同步應該用“推”還是“拉”?

          IM群聊消息如此復雜,如何保證不丟不重?

          談談移動端 IM 開發(fā)中登錄請求的優(yōu)化

          移動端IM登錄時拉取數(shù)據(jù)如何作到省流量?

          通俗易懂:基于集群的移動端IM接入層負載均衡方案分享

          微信對網(wǎng)絡影響的技術試驗及分析(論文全文)

          本文已同步發(fā)布于“即時通訊技術圈”公眾號。

          ▲ 本文在公眾號上的鏈接是:點此進入。同步發(fā)布鏈接是:http://www.52im.net/thread-3445-1-1.html

          posted @ 2021-03-22 16:10 Jack Jiang 閱讀(204) | 評論 (0)編輯 收藏

          1、引言

          經(jīng)歷過稍有些規(guī)模的IM系統(tǒng)開發(fā)的同行們都有體會,要想實現(xiàn)大規(guī)模并發(fā)IM(比如億級用戶和數(shù)十億日消息量這樣的規(guī)模),在架構設計上需要一些額外的考慮,尤其是要解決用戶高并發(fā)、服務高可用,架構和實現(xiàn)細節(jié)上都需要不短時間的打磨。

          我在過往的工作經(jīng)歷里,親手設計和實現(xiàn)了一套億級用戶量的IM,平臺上線并經(jīng)過6年多的驗證,穩(wěn)定性和可用性被驗證完全達到預期。

          這套IM系統(tǒng),從上線至今已6年有余,本人也已經(jīng)離職創(chuàng)業(yè)近2年,但當初設計和開發(fā)這套系統(tǒng)時積累和收獲了大量的第一手實踐經(jīng)驗和技術心得。

          因此,想借本文把當時的架構設計經(jīng)歷記錄下來,作為同行交流和參考,希望能提供一些啟發(fā),少走彎路。

          本文已同步發(fā)布于“即時通訊技術圈”公眾號,歡迎關注。公眾號上的鏈接是:點此進入

          2、系列文章

          為了更好以進行內容呈現(xiàn),本文拆分兩了上下兩篇。

          本文是2篇文章中的第1篇:

          一套億級用戶的IM架構技術干貨(上篇):整體架構、服務拆分等》(本文)

          《一套億級用戶的IM架構技術干貨(下篇):可靠性、有序性、弱網(wǎng)優(yōu)化等(稍后發(fā)布...)》

          本篇主要總結和分享這套IM架構的總體設計和服務拆分等。

          3、原作者

          本文基于鄧昀澤的“大規(guī)模并發(fā)IM服務架構設計”一文進行的擴展和修訂,感謝原作者的分享。

          鄧昀澤:畢業(yè)于北京航空航天大學,現(xiàn)藍貓微會創(chuàng)始人兼CEO,曾就職于美團、YY語音、微軟和金山軟件等公司,有十多年研發(fā)管理經(jīng)驗。

          4、技術指標

          在這套IM系統(tǒng)的架構上,技術上我們堅持高要求,經(jīng)過數(shù)年的驗證,也確實達到了設計預期。

          這4大技術指標是:

          具體解釋就是:

          • 1)高可靠:確保不丟消息;
          • 2)高可用:任意機房或者服務器掛掉,不影響服務;
          • 3)實時性:不管用戶在哪里,在線用戶消息在1秒內達到(我們實際是75%消息可以做到120ms);
          • 4)有序性:確保用戶消息的有序性,不會出現(xiàn)發(fā)送和接受的亂序。

          5、架構拆分

          從整體架構上來說,億級用戶量的IM架構整體上偏復雜。

          傳統(tǒng)開源的IM服務喜歡把所有服務做到1-2個服務里(Connector+Service模型),這樣帶來的問題比較嚴重。

          傳統(tǒng)開源的IM的問題主要體現(xiàn)在:

          • 1)服務代碼復雜,難以持續(xù)開發(fā)和運維;
          • 2)單一業(yè)務邏輯出問題,可能會影響到其它邏輯,導致服務的全面不可用。

          因此,我在做架構設計的時候盡量追求微服務化。即把整體架構進行分拆為子系統(tǒng),然后子系統(tǒng)內按照業(yè)務邏輯分拆為微服務。

          系統(tǒng)拆分如下圖:

          4個子系統(tǒng)的職責是:

          • 1)IM業(yè)務系統(tǒng):服務IM相關的業(yè)務邏輯(比如好友關系、群關系、用戶信息等);
          • 2)信令系統(tǒng):負責用戶登錄,用戶在線狀態(tài)的維護,以及在線用戶的下行推送;
          • 3)推送系統(tǒng):負責消息的在線推送和離線推送;
          • 4)存儲系統(tǒng):負責消息和文件的存儲和查詢;

          其中:信令系統(tǒng)和推送系統(tǒng)是基礎設施,不只是可以為IM業(yè)務服務,也可以承載其它類似的業(yè)務邏輯(比如客服系統(tǒng))。

          在部署層面:采用存儲3核心機房,信令和推送節(jié)點按需部署的方式(國內業(yè)務推薦8-10個點)。實際上我們只做了了北京3個機房,上海1個機房和香港一個機房的部署,就基本上滿足了大陸+香港的業(yè)務需求。

          下面將逐個介紹這4個子系統(tǒng)的細節(jié)方面。

          6、IM業(yè)務系統(tǒng)

          一說到IM,很多人腦海里跳出的第一個關鍵就是“即時通信”,技術上理所當然的聯(lián)想到了socket,也就是大家成天嘴上說的:“長連接”。換句話說,很多對IM不了解或了解的不多的人,認為IM里的所有數(shù)據(jù)交互、業(yè)務往來都是通過“長連接”來實現(xiàn)的,這樣話,對于本文章中拆分出的“IM業(yè)務系統(tǒng)”就有點不理解了。

          實際上,早期的IM(比如20年前的QQ、MSN、ICQ),確實所有數(shù)據(jù)基本都是通過“長連接”(也就是程序員所說的“socket”)實現(xiàn)。

          但如今,移動端為主端的IM時代,IM系統(tǒng)再也不是一個條“長連接”走天下。

          現(xiàn)在,一個典型的IM系統(tǒng)數(shù)據(jù)往來通常拆分成兩種服務:

          • 1)socket長連接服務(也就是本文中的“推送服務”);
          • 2)http短連接服務(就是最常用的http rest接口那些,也就是本文中的“IM業(yè)務系統(tǒng)”)。

          通俗一點,也也就現(xiàn)在的IM系統(tǒng),通常都是長、短連接配合一起實現(xiàn)的。 

          比如論壇里很多熱門技術方案都是這樣來做的,比如最典型的這兩篇:《IM單聊和群聊中的在線狀態(tài)同步應該用“推”還是“拉”?》、《IM消息送達保證機制實現(xiàn)(二):保證離線消息的可靠投遞》,文記里提到的“推”其實就是走的“長連接”、“拉”就上指的http短連接。

          對于socket長連接服務就沒什么好說,就是大家最常理解的那樣。

          IM業(yè)務系統(tǒng)詳細來說,就是專注處理IM相關的業(yè)務邏輯,比如:

          • 1)維護用戶數(shù)據(jù):用戶基本信息等;
          • 2)維護好友關系:好友請求、好友列表、好友信息等;
          • 3)維護群組信息:群創(chuàng)建、解散、成員管理等;
          • 4)提供數(shù)據(jù):離線拉取、歷史記錄同步;
          • 5)其它邏輯:比如通過存儲和推送系統(tǒng),存儲消息和發(fā)送通知;

          按照微服務的原則,IM業(yè)務系統(tǒng)也被分拆為多個服務,比如:

          • 1)GInfo服務:群組信息維護;
          • 2)IM服務:處理1V1消息;
          • 3)GIM服務:處理群組消息。

          7、信令系統(tǒng)

          7.1 基本情況

          信令系統(tǒng)主要職責是3部分: 

           

          1)維護用戶在線狀態(tài):

          因為用戶規(guī)模龐大,必然是多個集群,每個集群多臺服務器為用戶提供服務。

          考慮到服務器運維的復雜性,我們要假定任何一個集群,任何一個服務器都可能會掛掉,而且在這種情況下要能夠繼續(xù)為用戶提供服務。

          在這種情況下,如果用戶A給用戶B發(fā)消息,我們需要知道用戶B在哪個服務器上,才能把消息正確推送給用戶B。用戶在哪個信令服務,這個信息就是在線狀態(tài)數(shù)據(jù)。

          2)下行消息推送:

          跟上一個職責有關,用戶在線的時候,如果有其它用戶給他發(fā)消息,那就最好不要走離線推送,而是走在線推送。

          在線推送的最后一個環(huán)節(jié),是把用戶消息推送給用戶設備,因為就需要知道用戶登錄到哪個服務器上。

          3)業(yè)務分發(fā):

          信令服務不只可以處理IM請求,也可以處理其它類型的業(yè)務請求。為了處理不同的業(yè)務,就需要有分發(fā)能力。

          具體做法是通過一個SVID(service id)來實現(xiàn),不同的業(yè)務攜帶不同的SVID,信令服務就知道如何分發(fā)了。

          用戶通過登錄服務把數(shù)據(jù)(比如IM消息)發(fā)送到信令系統(tǒng),信令系統(tǒng)根據(jù)SVID轉發(fā)給IM系統(tǒng)。不管后臺有多少個業(yè)務,用戶只需要一條鏈接到信令。

          7.2 服務拆分

          信令系統(tǒng)為了實現(xiàn)以上這3個職責,同時要確保我們服務可平行擴展的能力和穩(wěn)定性,在實際的技術實現(xiàn)上,我們實際上把信令服務分拆為3個服務模塊。

          如下圖所示: 

          下面將逐個介紹這3個子服務。

          7.3 Login服務

          Login服務主要負責維護用戶長鏈接:

          • 1)每個用戶一條鏈接到Login服務,并按時間發(fā)心跳包給Login服務;
          • 2)服務定時檢查用戶鏈接狀態(tài)和心跳包,比如發(fā)現(xiàn)2個心跳周期都沒收到心跳,就認為用戶掉線了(有假在線問題,有興趣同學可回貼討論)。

          Login服務收到用戶登錄請求以后,驗證uid/cookie,如果成功就把這個用戶的登錄信息發(fā)送給online。

          此過程主要記錄的信息包含:

          • 1)uid(用戶id);
          • 2)Login服務器IP/Port;
          • 3)Route服務器的IP/Port。

          如果用戶發(fā)送IM消息,先發(fā)送到Login,Login轉發(fā)給Route,Route根據(jù)服務的類型(SVID),發(fā)現(xiàn)是IM協(xié)議就發(fā)送給后端的IM服務。

          Login對并發(fā)要求比較高,一般要支持TCP+UDP+Websocket幾種方式,單服務可以做到10-250萬之間。從服務穩(wěn)定性角度觸發(fā),建議是控制VM的CPU/內存,單服務器以20-50萬為合適。

          Login服務器本身沒有狀態(tài),任何一個Login服務斷掉,用戶端檢測到以后重連另一個Login服務器就可以了,對整體服務可靠性基本沒有影響。

          7.4 Online服務

          Online服務主要負責維護用戶的在線信息:

          • 1)如果用戶掉線,Online服務里信息就是空;
          • 2)如果用戶在線,Online就能找到用戶登錄在哪個集群,哪個Login服務器上。

          Online業(yè)務相對簡單:多個Login服務器會連接到Online,定期同步用戶登錄和離線信息。

          Online主要職責是:把用戶狀態(tài)信息存儲在Redis集群里。因此也是無狀態(tài)的,任何一個Online服務掛掉,不影響整體服務能力。

          如果集群規(guī)模不大,用戶規(guī)模也不大,Online服務也可以收到Login服務里去。

          如果規(guī)模比較大,建議分拆出來,一方面簡化Login的邏輯復雜度,同時避免寫Redis的慢操作放在Login服務里。因為Login要同時處理50萬以上的并發(fā)鏈接,不適合在循環(huán)里嵌入慢操作。

          7.5 Route服務

          Route服務的設計核心,是作為信令系統(tǒng)跟其它子系統(tǒng)的交互層。Route下接Login服務,可以接受用戶業(yè)務信息(IM),也可以往用戶推送下行消息。

          多個后端業(yè)務系統(tǒng)可以接入到Route,按照服務類型(SVID, service id)注冊。比如IM服務可以接入到Route, 注冊SVID_IM。這樣Login接收到SVID=SVID_IM的消息,轉發(fā)給Route,Route就可以根據(jù)SVID轉發(fā)給IM相關的服務。

          Route簡單的根據(jù)SVID做轉發(fā),不處理具體的業(yè)務邏輯,因此也是無狀態(tài)的。一個信令集群可以有多個Route服務,任何服務掛了不影響整體服務能力。

          8、推送系統(tǒng)

          推送系統(tǒng)的核心任務:是接收到給用戶發(fā)送下行消息的請求以后,去信令服務查詢用戶是否在線,如果在線走信令推送,如果不在線走離線推送(如iOS的APNS、華為推送、小米推送等)。

          因為推送服務可能出現(xiàn)大規(guī)模并發(fā)蜂擁,比如大群激烈討論的時候,會觸發(fā)億級的TPS。因此推送服務用Kafka做了削峰。

          我在實際的技術實現(xiàn)上,將推送系統(tǒng)進行了如下細分: 

          具體就是:

          • 1)PushProxy:接受用戶的推送請求,寫入Kafka;
          • 2)Kafka:緩存推送服務;
          • 3)PushServer:從Kafka獲取推送請求,判斷用戶是否在線;
          • 4)PushWorker:真正推送給信令或者APNS,華為推送等。

          這里同樣,除了Kafka以外每個服務都是無狀態(tài)的,因為也可以實現(xiàn)平行擴展和容錯,任何服務掛掉不影響整體服務可用性。

          9、存儲系統(tǒng)

          存儲服務主要是負責消息的存儲和查詢,因為消息量巨大,對存儲服務的并發(fā)能力和存儲量要求巨大。

          為了平衡性能、空間和成本,存儲服務按數(shù)據(jù)的熱度進行了分級和區(qū)別對待。

          具體是:

          • 1)短期消息(7天):存儲在Redis里;
          • 2)近期消息(1-3個月):存儲在Mysql里,以備用戶實時查詢;
          • 3)歷史信息:存儲在HBase里,作為歷史數(shù)據(jù)慢查詢。

          同時,為了應對超大群的大量消息處理,存儲服務在實際的技術實現(xiàn)上,也做了比較細的分拆。

          存儲服務具體拆分如下圖:

          具體的業(yè)務劃分就是:

          • 1)MsgProxy:負責接受IM子系統(tǒng)的存儲請求,寫入Kafka;
          • 2)MsgWriter:從Kafka獲取寫請求,按需寫入Redis和Mysql;
          • 3)MsgReader:接受用戶的消息查詢請求,從Redis,Mysql或者HBase讀數(shù)據(jù);
          • 4)運維工具:主要是數(shù)據(jù)庫的運維需求。

          消息隊列(Kafka)在這里角色比較重要,因為對于高并發(fā)請求(100萬人公眾號),需要通過消息隊列來做削峰和并行。

          在具體部署上:可能是3-4個MsgProxy,后端可以對應15個左右的MsgWriter。MsgWriter是比較慢的,需要同時操作多個數(shù)據(jù)庫,還要保證操作的原子性。

          10、本篇小結

          本篇主要總結了這套億級用戶量IM系統(tǒng)的總體架構設計,為了高性能和橫向擴展性,基于微信的理念將整個架構在實現(xiàn)上分成了4個子系統(tǒng),分別是:IM業(yè)務系統(tǒng)、信令系統(tǒng)、推送系統(tǒng)、存儲系統(tǒng)。

          針對這4個子系統(tǒng),在實際的技術應用層上,又進行了進一步的服務拆分和細化,使得整個架構伸縮性大大增強。

          —— 下篇《一套億級用戶的IM架構技術干貨(下篇):可靠性、有序性、弱網(wǎng)優(yōu)化等》稍后發(fā)布,敬請期待 ——

          附錄:相關文章

          淺談IM系統(tǒng)的架構設計

          簡述移動端IM開發(fā)的那些坑:架構設計、通信協(xié)議和客戶端

          一套海量在線用戶的移動端IM架構設計實踐分享(含詳細圖文)》(* 力薦

          一套原創(chuàng)分布式即時通訊(IM)系統(tǒng)理論架構方案》(* 力薦

          從零到卓越:京東客服即時通訊系統(tǒng)的技術架構演進歷程》(* 力薦

          蘑菇街即時通訊/IM服務器開發(fā)之架構選擇

          騰訊QQ1.4億在線用戶的技術挑戰(zhàn)和架構演進之路PPT

          微信后臺基于時間序的海量數(shù)據(jù)冷熱分級架構設計實踐

          移動端IM中大規(guī)模群消息的推送如何保證效率、實時性?

          現(xiàn)代IM系統(tǒng)中聊天消息的同步和存儲方案探討》(* 力薦

          以微博類應用場景為例,總結海量社交系統(tǒng)的架構設計步驟

          一套高可用、易伸縮、高并發(fā)的IM群聊、單聊架構方案設計實踐》(* 力薦

          社交軟件紅包技術解密(一):全面解密QQ紅包技術方案——架構、技術實現(xiàn)等

          社交軟件紅包技術解密(二):解密微信搖一搖紅包從0到1的技術演進

          社交軟件紅包技術解密(三):微信搖一搖紅包雨背后的技術細節(jié)

          社交軟件紅包技術解密(四):微信紅包系統(tǒng)是如何應對高并發(fā)的

          社交軟件紅包技術解密(五):微信紅包系統(tǒng)是如何實現(xiàn)高可用性的

          社交軟件紅包技術解密(六):微信紅包系統(tǒng)的存儲層架構演進實踐

          社交軟件紅包技術解密(七):支付寶紅包的海量高并發(fā)技術實踐

          社交軟件紅包技術解密(八):全面解密微博紅包技術方案

          從游擊隊到正規(guī)軍(一):馬蜂窩旅游網(wǎng)的IM系統(tǒng)架構演進之路》(* 力薦

          從游擊隊到正規(guī)軍(二):馬蜂窩旅游網(wǎng)的IM客戶端架構演進和實踐總結

          從游擊隊到正規(guī)軍(三):基于Go的馬蜂窩旅游網(wǎng)分布式IM系統(tǒng)技術實踐

          瓜子IM智能客服系統(tǒng)的數(shù)據(jù)架構設計(整理自現(xiàn)場演講,有配套PPT)

          阿里釘釘技術分享:企業(yè)級IM王者——釘釘在后端架構上的過人之處

          微信后臺基于時間序的新一代海量數(shù)據(jù)存儲架構的設計實踐

          一套億級用戶的IM架構技術干貨(上篇):整體架構、服務拆分等

          >> 更多同類文章 ……

           

          本文已同步發(fā)布于“即時通訊技術圈”公眾號。

          ▲ 本文在公眾號上的鏈接是:點此進入。同步發(fā)布鏈接是:http://www.52im.net/thread-3393-1-1.html

          posted @ 2021-03-15 23:07 Jack Jiang 閱讀(485) | 評論 (0)編輯 收藏

               摘要: 本文由微信開發(fā)團隊工程師“ kellyliang”原創(chuàng)發(fā)表于“微信后臺團隊”公眾號,收錄時有修訂和改動。1、引言隨著直播和類直播場景在微信內的增長,這些業(yè)務對臨時消息(在線狀態(tài)時的實時消息)通道的需求日益增長,直播聊天室組件應運而生。直播聊天室組件是一個基于房間的臨時消息信道,主要提供消息收發(fā)、在線狀態(tài)統(tǒng)計等功能。本文將回顧微信直播聊天室單房間海量用...  閱讀全文

          posted @ 2021-03-06 17:08 Jack Jiang 閱讀(304) | 評論 (0)編輯 收藏

               摘要: 本文引用了“一文讀懂什么是進程、線程、協(xié)程”一文的主要內容,感謝原作者的無私分享。1、系列文章引言1.1 文章目的作為即時通訊技術的開發(fā)者來說,高性能、高并發(fā)相關的技術概念早就了然與胸,什么線程池、零拷貝、多路復用、事件驅動、epoll等等名詞信手拈來,又或許你對具有這些技術特征的技術框架比如:Java的Netty、Php的workman、Go的gnet等熟練掌握。但真正到...  閱讀全文

          posted @ 2021-03-03 13:02 Jack Jiang 閱讀(229) | 評論 (0)編輯 收藏

          本文原題“你管這破玩意兒叫TCP?”,由閃客sun分享,轉載請聯(lián)系作者。

          1、引言

          網(wǎng)絡編程能力對于即時通訊技術開發(fā)者來說是基本功,而計算機網(wǎng)絡又是網(wǎng)絡編程的理論根基,因而深刻準確地理解計算機網(wǎng)絡知識顯然能夯實你的即時通訊應用的實踐品質。

          本文風格類似于《網(wǎng)絡編程懶人入門》、《腦殘式網(wǎng)絡編程入門》兩個系列,但通俗又不失內涵,簡潔又不簡陋,非常適合對計算機網(wǎng)絡知識有向往但又有懼怕的網(wǎng)絡編程愛好者們閱讀,希望能給你帶來不一樣的網(wǎng)絡知識入門視角。

          本篇將運用通俗易懂的語言,配上細致精確的圖片動畫,循序漸進地引導你理解TCP協(xié)議的主要特性和技術原理,讓TCP協(xié)議的學習不再如此枯燥和生澀,非常適合入門者閱讀。

          本文已同步發(fā)布于“即時通訊技術圈”公眾號,歡迎關注。公眾號上的鏈接是:點此進入

          2、系列文章

          本文是該系列文章中的第2篇:

          本文主要涉及計算機網(wǎng)絡的傳輸層,希望讓TCP協(xié)議的學習不再枯燥和生澀。

          3、初識傳輸層

          你是一臺電腦,你的名字叫 A。

          網(wǎng)絡編程入門從未如此簡單(二):假如你來設計TCP協(xié)議,會怎么做?_1-1.png
          經(jīng)過上篇《假如你來設計網(wǎng)絡,會怎么做?》的一番折騰,只要你知道另一位伙伴 B 的 IP 地址,且你們之間的網(wǎng)絡是通的,無論多遠,你都可以將一個數(shù)據(jù)包發(fā)送給你的伙伴 B。

          網(wǎng)絡編程入門從未如此簡單(二):假如你來設計TCP協(xié)議,會怎么做?_1-2.png

          上篇中分享的這就是物理層、數(shù)據(jù)鏈路層、網(wǎng)絡層這三層所做的事情。

          站在第四層的你,就可以不要臉地利用下三層所做的鋪墊,隨心所欲地發(fā)送數(shù)據(jù),而不必擔心找不到對方了。

          網(wǎng)絡編程入門從未如此簡單(二):假如你來設計TCP協(xié)議,會怎么做?_1-3.gif

          雖然你此時還什么都沒干,但你還是給自己這一層起了個響亮的名字,叫做傳輸層。

          你本以為自己所在的第四層萬事大吉,啥事沒有,但很快問題就接踵而至。

          4、問題來了

          前三層協(xié)議只能把數(shù)據(jù)包從一個主機搬到另外一臺主機,但是到了目的地以后,數(shù)據(jù)包具體交給哪個程序(進程)呢?

          網(wǎng)絡編程入門從未如此簡單(二):假如你來設計TCP協(xié)議,會怎么做?_2-1.png

          所以:你需要把通信的進程區(qū)分開來,于是就給每個進程分配一個數(shù)字編號,你給它起了一個響亮的名字:端口號。

          網(wǎng)絡編程入門從未如此簡單(二):假如你來設計TCP協(xié)議,會怎么做?_2-2.png

          然后:你在要發(fā)送的數(shù)據(jù)包上,增加了傳輸層的頭部:源端口號與目標端口號。

          網(wǎng)絡編程入門從未如此簡單(二):假如你來設計TCP協(xié)議,會怎么做?_2-3.png

          OK,這樣你將原本主機到主機的通信,升級為了進程和進程之間的通信。

          你沒有意識到,你不知不覺實現(xiàn)了UDP協(xié)議

          當然 UDP 協(xié)議中不光有源端口和目標端口,還有數(shù)據(jù)包長度和校驗值,我們暫且略過。

          就這樣,你用 UDP 協(xié)議無憂無慮地同 B 進行著通信,一直沒發(fā)生什么問題。

          網(wǎng)絡編程入門從未如此簡單(二):假如你來設計TCP協(xié)議,會怎么做?_2-4.gif

          但很快,你發(fā)現(xiàn)事情變得非常復雜 ... ...

          5、丟包問題

          由于網(wǎng)絡的不可靠,數(shù)據(jù)包可能在半路丟失,而 A 和 B 卻無法察覺。

          網(wǎng)絡編程入門從未如此簡單(二):假如你來設計TCP協(xié)議,會怎么做?_3-1.gif

          對于丟包問題,只要解決兩個事就好了。

          第一個:A 怎么知道包丟了?
          答案是:讓 B 告訴 A。

          第二個:丟了的包怎么辦?
          答案是:重傳。

          于是你設計了如下方案:A 每發(fā)一個包,都必須收到來自 B 的確認(ACK),再發(fā)下一個,否則在一定時間內沒有收到確認,就重傳這個包。

          網(wǎng)絡編程入門從未如此簡單(二):假如你來設計TCP協(xié)議,會怎么做?_3-2.gif

          你管它叫停止等待協(xié)議。

          只要按照這個協(xié)議來,雖然 A 無法保證 B 一定能收到包,但 A 能夠確認 B 是否收到了包,收不到就重試,盡最大努力讓這個通信過程變得可靠,于是你們現(xiàn)在的通信過程又有了一個新的特征,可靠交付。

          6、效率問題

          停止等待雖然能解決問題,但是效率太低了。

          A 原本可以在發(fā)完第一個數(shù)據(jù)包之后立刻開始發(fā)第二個數(shù)據(jù)包,但由于停止等待協(xié)議,A 必須等數(shù)據(jù)包到達了 B ,且 B 的 ACK 包又回到了 A,才可以繼續(xù)發(fā)第二個數(shù)據(jù)包。這效率慢得可不是一點兩點。

          于是:你對這個過程進行了改進,采用流水線的方式,不再傻傻地等。

          網(wǎng)絡編程入門從未如此簡單(二):假如你來設計TCP協(xié)議,會怎么做?_4-1.gif

          7、順序問題

          但是網(wǎng)路是復雜的、不可靠的。

          這導致的問題是:有的時候 A 發(fā)出去的數(shù)據(jù)包,分別走了不同的路由到達 B,可能無法保證和發(fā)送數(shù)據(jù)包時一樣的順序。

          網(wǎng)絡編程入門從未如此簡單(二):假如你來設計TCP協(xié)議,會怎么做?_5-1.gif

          對應于我們的例子:在流水線中有多個數(shù)據(jù)包和ACK包在亂序流動,他們之間對應關系就亂掉了。

          如果回到上面的停止等待協(xié)議,那么A 每收到一個包的確認(ACK)再發(fā)下一個包,那就根本不存在順序問題。但,應該有更好的辦法吧?

          是的,更好的辦法就是:A 在發(fā)送的數(shù)據(jù)包中增加一個序號(seq),同時 B 要在 ACK 包上增加一個確認號(ack)。這樣不但解決了停止等待協(xié)議的效率問題,也通過這樣標序號的方式解決了順序問題。

          網(wǎng)絡編程入門從未如此簡單(二):假如你來設計TCP協(xié)議,會怎么做?_5-2.gif

          而 B 這個確認號意味深長:比如 B 發(fā)了一個確認號為 ack = 3,它不僅僅表示 A 發(fā)送的序號為 2 的包收到了,還表示 2 之前的數(shù)據(jù)包都收到了。這種方式叫累計確認累計應答

          網(wǎng)絡編程入門從未如此簡單(二):假如你來設計TCP協(xié)議,會怎么做?_5-3.gif

          注意:實際上 ack 的號是收到的最后一個數(shù)據(jù)包的序號 seq + 1,也就是告訴對方下一個應該發(fā)的序號是多少。但圖中為了便于理解,ack 就表示收到的那個序號,不必糾結。

          8、流量問題

          有的時候,A 發(fā)送數(shù)據(jù)包的速度太快,而 B 的接收能力不夠,但 B 卻沒有告知 A 這個情況。

          網(wǎng)絡編程入門從未如此簡單(二):假如你來設計TCP協(xié)議,會怎么做?_6-1.gif

          怎么解決呢?

          很簡單:B 告訴 A 自己的接收能力,A 根據(jù) B 的接收能力,相應控制自己的發(fā)送速率就好了。

          B 怎么告訴 A 呢?B 跟 A 說"我很強"這三個字么?那肯定不行,得有一個嚴謹?shù)囊?guī)范。

          于是 B 決定:每次發(fā)送數(shù)據(jù)包給 A 時,順帶傳過來一個值,叫窗口大小(win),這個值就表示 B 的接收能力

          同理:每次 A 給 B 發(fā)包時也帶上自己的窗口大小,表示 A 的接收能力。

          網(wǎng)絡編程入門從未如此簡單(二):假如你來設計TCP協(xié)議,會怎么做?_6-2.gif

          B 告訴了 A 自己的窗口大小值,A 怎么利用它去做 A 這邊發(fā)包的流量控制呢?

          很簡單:假如 B 給 A 傳過來的窗口大小 win = 5,那 A 根據(jù)這個值,把自己要發(fā)送的數(shù)據(jù)分成這么幾類。

          網(wǎng)絡編程入門從未如此簡單(二):假如你來設計TCP協(xié)議,會怎么做?_6-3.png

          圖片過于清晰,就不再文字解釋了。

          當 A 不斷發(fā)送數(shù)據(jù)包時,已發(fā)送的最后一個序號就往右移動,直到碰到了窗口的上邊界,此時 A 就無法繼續(xù)發(fā)包,達到了流量控制。

          網(wǎng)絡編程入門從未如此簡單(二):假如你來設計TCP協(xié)議,會怎么做?_6-4.gif

          但是:當 A 不斷發(fā)包的同時,A 也會收到來自 B 的確認包,此時整個窗口會往右移動,因此上邊界也往右移動,A 就能發(fā)更多的數(shù)據(jù)包了。

          網(wǎng)絡編程入門從未如此簡單(二):假如你來設計TCP協(xié)議,會怎么做?_6-5.gif

          以上都是在窗口大小不變的情況下。而 B 在發(fā)給 A 的 ACK 包中,每一個都可以重新設置一個新的窗口大小,如果 A 收到了一個新的窗口大小值,A 會隨之調整。

          如果 A 收到了比原窗口值更大的窗口大小,比如 win = 6,則 A 會直接將窗口上邊界向右移動 1 個單位。

          網(wǎng)絡編程入門從未如此簡單(二):假如你來設計TCP協(xié)議,會怎么做?_6-6.gif

          如果 A 收到了比原窗口值小的窗口大小,比如 win = 4,則 A 暫時不會改變窗口大小,更不會將窗口上邊界向左移動,而是等著 ACK 的到來,不斷將左邊界向右移動,直到窗口大小值收縮到新大小為止。

          網(wǎng)絡編程入門從未如此簡單(二):假如你來設計TCP協(xié)議,會怎么做?_6-7.gif

          OK,終于將流量控制問題解決得差不多了,你看著上面一個個小動圖,給這個窗口起了一個更生動的名字:滑動窗口。

          9、擁塞問題

          但有的時候,不是 B 的接受能力不夠,而是網(wǎng)絡不太好,造成了網(wǎng)絡擁塞。

          網(wǎng)絡編程入門從未如此簡單(二):假如你來設計TCP協(xié)議,會怎么做?_7-1.gif

          擁塞控制與流量控制有些像,但流量控制是受 B 的接收能力影響,而擁塞控制是受網(wǎng)絡環(huán)境的影響。

          擁塞控制的解決辦法依然是通過設置一定的窗口大小。只不過,流量控制的窗口大小是 B 直接告訴 A 的,而擁塞控制的窗口大小按理說就應該是網(wǎng)絡環(huán)境主動告訴 A。

          但網(wǎng)絡環(huán)境怎么可能主動告訴 A 呢?只能 A 單方面通過試探,不斷感知網(wǎng)絡環(huán)境的好壞,進而確定自己的擁塞窗口的大小。

          網(wǎng)絡編程入門從未如此簡單(二):假如你來設計TCP協(xié)議,會怎么做?_7-2.gif

          擁塞窗口大小的計算有很多復雜的算法,就不在本文中展開了(有興趣可以深入閱讀《[通俗易懂]深入理解TCP協(xié)議(下):RTT、滑動窗口、擁塞處理》)。

          假如擁塞窗口的大小為  cwnd,上一部分流量控制的滑動窗口的大小為 rwnd,那么窗口的右邊界受這兩個值共同的影響,需要取它倆的最小值。

          窗口大小 = min(cwnd, rwnd)

          含義很容易理解:當 B 的接受能力比較差時,即使網(wǎng)絡非常通暢,A 也需要根據(jù) B 的接收能力限制自己的發(fā)送窗口。當網(wǎng)絡環(huán)境比較差時,即使 B 有很強的接收能力,A 也要根據(jù)網(wǎng)絡的擁塞情況來限制自己的發(fā)送窗口。正所謂受其短板的影響嘛~

          10、連接問題

          有的時候,B 主機的相應進程還沒有準備好或是掛掉了,A 就開始發(fā)送數(shù)據(jù)包,導致了浪費。

          網(wǎng)絡編程入門從未如此簡單(二):假如你來設計TCP協(xié)議,會怎么做?_8-1.gif

          這個問題在于:A 在跟 B 通信之前,沒有事先確認 B 是否已經(jīng)準備好,就開始發(fā)了一連串的信息。就好比你和另一個人打電話,你還沒有"喂"一下確認對方有沒有在聽,你就巴拉巴拉說了一堆。

          這個問題該怎么解決呢?

          地球人都知道:三次握手嘛!

          • A:我準備好了(SYN)
          • B:我知道了(ACK),我也準備好了(SYN)
          • A:我知道了(ACK)

          網(wǎng)絡編程入門從未如此簡單(二):假如你來設計TCP協(xié)議,會怎么做?_8-2.gif

          A 與 B 各自在內存中維護著自己的狀態(tài)變量,三次握手之后,雙方的狀態(tài)都變成了連接已建立(ESTABLISHED)。

          雖然就只是發(fā)了三次數(shù)據(jù)包,并且在各自的內存中維護了狀態(tài)變量,但這么說總覺得太 low,你看這個過程相當于雙方建立連接的過程,于是你靈機一動,就叫它面向連接吧。

          注意:這個連接是虛擬的,是由 A 和 B 這兩個終端共同維護的,在網(wǎng)絡中的設備根本就不知道連接這回事兒!

          但凡事有始就有終,有了建立連接的過程,就要考慮釋放連接的過程。

          這就是網(wǎng)絡編程中耳熟能詳?shù)乃拇螕]手啦!

          • A:再見,我要關閉了(FIN)
          • B:我知道了(ACK)。給 B 一段時間把自己的事情處理完...
          • B:再見,我要關閉了(FIN)
          • A:我知道了(ACK)

          網(wǎng)絡編程入門從未如此簡單(二):假如你來設計TCP協(xié)議,會怎么做?_8-3.gif

          11、小結一下

          以上講述的,就是 TCP 協(xié)議的核心思想,上面過程中需要傳輸?shù)男畔ⅲ腕w現(xiàn)在 TCP 協(xié)議的頭部,這里放上最常見的 TCP 協(xié)議頭解讀的圖。

          網(wǎng)絡編程入門從未如此簡單(二):假如你來設計TCP協(xié)議,會怎么做?_9-1.png

          不知道你現(xiàn)在再看下面這句話,是否能理解:

          TCP 是面向連接的、可靠的、基于字節(jié)流的傳輸層通信協(xié)議。

          “面向連接、可靠”,這兩個詞通過上面的講述很容易理解,那什么叫做基于字節(jié)流呢?

          很簡單:TCP 在建立連接時,需要告訴對方 MSS(最大報文段大小)。

          也就是說:如果要發(fā)送的數(shù)據(jù)很大,在 TCP 層是需要按照 MSS 來切割成一個個的 TCP 報文段 的。

          切割的時候我才不管你原來的數(shù)據(jù)表示什么意思,需要在哪里斷句啥的,我就把它當成一串毫無意義的字節(jié),在我想要切割的地方咔嚓就來一刀,標上序號,只要接收方再根據(jù)這個序號拼成最終想要的完整數(shù)據(jù)就行了。

          在我 TCP 傳輸這里,我就把它當做一個個的字節(jié),也就是基于字節(jié)流的含義了。

          網(wǎng)絡編程入門從未如此簡單(二):假如你來設計TCP協(xié)議,會怎么做?_10-1.png

          12、寫在最后

          一提到 TCP,可能很多人都想起被三次握手和四次揮手所支配的恐懼。

          但其實你跟著本文中的思路你就會發(fā)現(xiàn),三次握手與四次揮手只占 TCP 所解決的核心問題中很小的一部分,只是因為它在面試中很適合作為知識點進行考察,所以在很多人的印象中就好像 TCP 的核心就是握手和揮手似的。

          本文希望你能從問題出發(fā),真正理解 TCP 所想要解決的問題,你會發(fā)現(xiàn)很多原理就好像生活常識一樣順其自然,并不復雜,希望你有收獲~

          最后,如果對TCP的理解仍存在疑惑,可以繼續(xù)閱讀以下精選的資料:

          本文已同步發(fā)布于“即時通訊技術圈”公眾號。

          ▲ 本文在公眾號上的鏈接是:點此進入。同步發(fā)布鏈接是:http://www.52im.net/thread-3339-1-1.html

          posted @ 2021-02-24 12:47 Jack Jiang 閱讀(314) | 評論 (0)編輯 收藏

          僅列出標題
          共50頁: First 上一頁 24 25 26 27 28 29 30 31 32 下一頁 Last 
          Jack Jiang的 Mail: jb2011@163.com, 聯(lián)系QQ: 413980957, 微信: hellojackjiang
          主站蜘蛛池模板: 宿州市| 腾冲县| 黔西县| 周宁县| 绥化市| 确山县| 仲巴县| 金秀| 合肥市| 锡林郭勒盟| 渭源县| 逊克县| 沈阳市| 桃园市| 南漳县| 平利县| 黔南| 阿图什市| 洛宁县| 汝阳县| 廊坊市| 旌德县| 修水县| 颍上县| 宜川县| 五莲县| 荣成市| 西安市| 蚌埠市| 屯昌县| 探索| 雷州市| 怀来县| 黑水县| 诸城市| 邛崃市| 达孜县| 舒兰市| 宜昌市| 盐源县| 马山县|