1、引言
好久沒寫技術文章了,今天這篇不是原理性文章,而是為大家分享一下由筆者主導開發實施的IM即時通訊聊天系統,針對大量離線消息(包括消息漫游)導致的用戶體驗問題的升級改造全過程。
文章中,我將從如下幾個方面進行介紹:
- 1)這款IM產品的主要業務及特點;
- 2)IM系統業務現狀和痛點;
- 3)升級改造之路;
- 4)消息ACK邏輯的優化。
下述內容都是根據筆者開發IM的親身經歷總結下來的寶貴經驗,干貨滿滿,期待你的點贊。
學習交流:
- 即時通訊/推送技術開發交流5群:215477170 [推薦]
- 移動端IM開發入門文章:《新手入門一篇就夠:從零開發移動端IM》
本文已同步發布于“即時通訊技術圈”公眾號,歡迎關注:
▲ 本文公眾號鏈接是:https://mp.weixin.qq.com/s/XHdt1IpCrdmaMDxL8WKn3w,原文鏈接是:http://www.52im.net/thread-3036-1-1.html
2、IM開發干貨系列文章
本文是系列文章中的第25篇,總目錄如下:
《IM消息送達保證機制實現(一):保證在線實時消息的可靠投遞》
《IM消息送達保證機制實現(二):保證離線消息的可靠投遞》
《如何保證IM實時消息的“時序性”與“一致性”?》
《IM單聊和群聊中的在線狀態同步應該用“推”還是“拉”?》
《IM群聊消息如此復雜,如何保證不丟不重?》
《一種Android端IM智能心跳算法的設計與實現探討(含樣例代碼)》
《移動端IM登錄時拉取數據如何作到省流量?》
《通俗易懂:基于集群的移動端IM接入層負載均衡方案分享》
《淺談移動端IM的多點登陸和消息漫游原理》
《IM開發基礎知識補課(一):正確理解前置HTTP SSO單點登陸接口的原理》
《IM開發基礎知識補課(二):如何設計大量圖片文件的服務端存儲架構?》
《IM開發基礎知識補課(三):快速理解服務端數據庫讀寫分離原理及實踐建議》
《IM開發基礎知識補課(四):正確理解HTTP短連接中的Cookie、Session和Token》
《IM群聊消息的已讀回執功能該怎么實現?》
《IM群聊消息究竟是存1份(即擴散讀)還是存多份(即擴散寫)?》
《IM開發基礎知識補課(五):通俗易懂,正確理解并用好MQ消息隊列》
《一個低成本確保IM消息時序的方法探討》
《IM開發基礎知識補課(六):數據庫用NoSQL還是SQL?讀這篇就夠了!》
《IM里“附近的人”功能實現原理是什么?如何高效率地實現它?》
《IM開發基礎知識補課(七):主流移動端賬號登錄方式的原理及設計思路》
《IM開發基礎知識補課(八):史上最通俗,徹底搞懂字符亂碼問題的本質》
《IM的掃碼登功能如何實現?一文搞懂主流應用的掃碼登陸技術原理》
《IM要做手機掃碼登陸?先看看微信的掃碼登錄功能技術原理》
《IM開發基礎知識補課(九):想開發IM集群?先搞懂什么是RPC!》
《IM開發實戰干貨:我是如何解決大量離線聊天消息導致客戶端卡頓的》(本文)
另外,如果您是IM開發初學者,強烈建議首先閱讀《新手入門一篇就夠:從零開發移動端IM》。
3、此IM產品的主要業務及特點
和傳統互聯網行業有所不同,筆者所在的公司(名字就不透露了)是一家做娛樂社交app的公司,包括小游戲、聊天、朋友圈feed等。
大家應該都有體會:游戲業務在技術上和產品形態上與電商、旅游等行業有著本質上的區別。
大部分做后端開發的朋友,都在開發接口??蛻舳嘶驗g覽器h5通過HTTP請求到我們后端的Controller接口,后端查數據庫等返回JSON給客戶端。大家都知道,HTTP協議有短連接、無狀態、三次握手四次揮手等特點。而像游戲、實時通信等業務反而很不適合用HTTP協議。
原因如下:
- 1)HTTP達不到實時通信的效果,可以用客戶端輪詢但是太浪費資源;
- 2)三次握手四次揮手有嚴重的性能問題;
- 3)無狀態。
比如說,兩個用戶通過App聊天,一方發出去的消息,對方要實時感知到消息的到來。兩個人或多個人玩游戲,玩家要實時看到對方的狀態,這些場景用HTTP根本不可能實現!因為HTTP只能pull(即“拉”),而聊天、游戲業務需要push(即“推”)。
4、IM系統業務現狀和痛點
4.1 業務現狀
筆者負責整個公司的實時聊天系統,類似與微信、QQ那樣,有私聊、群聊、發消息、語音圖片、紅包等功能。
下面我詳細介紹一下,整個聊天系統是如何運轉的。
首先:為了達到實時通信的效果,我們基于Netty開發了一套長鏈接網關gateway(擴展閱讀:《Netty干貨分享:京東京麥的生產級TCP網關技術實踐總結》),采用的協議是MQTT協議,客戶端登錄時App通過MQTT協議連接到gateway(NettyServer),然后通過MQTT協議把聊天消息push給NettyServer,NettyServer與NettyClient保持長鏈接,NettyClient用于處理業務邏輯(如敏感詞攔截、數據校驗等)處理,最后將消息push給NettyServer,再由NettyServer通過MQTT push給客戶端。
其次:客戶端與服務端想要正常通信,我們需要制定一套統一的協議。拿聊天舉例,我們要和對方聊天,需要通過uid等信息定位到對方的Channel(Netty中的通道,相當于一條socket連接),才能將消息發送給正確的客戶端,同時客戶端必須通過協議中的數據(uid、groupId等),將消息顯示在私聊或者群聊的會話中。
協議中主要字段如下(我們將數據編碼成protobuf格式進行傳輸):
{
"cmd":"chat",
"time":1554964794220,
"uid":"69212694",
"clientInfo":{
"deviceId":"b3b1519c-89ec",
"deviceInfo":"MI 6X"
},
"body":{
"v":1,
"msgId":"5ab2fe83-59ec-44f0-8adc-abf26c1e1029",
"chatType":1,
"ackFlg":1,
"from":"69212694",
"to":"872472068",
"time":1554964793813,
"msg":{
"message":"聊天消息"
}
}
}
補充說明:如果你不了Protobuf格式是什么,請詳讀《Protobuf通信協議詳解:代碼演示、詳細原理介紹等》。
如上json,協議主要字段包括:
如果客戶端不在線,我們服務端需要把發送的消息存儲在離線消息表中,等下次對方客戶端上線,服務端NettyServer通過長鏈接把離線消息push給客戶端。
4.2 業務痛點
隨著業務蓬勃發展,用戶的不斷增多,用戶創建的群、加入的群和好友不斷增多和聊天活躍度的上升,某些用戶不在線期間,產生大量的離線消息(尤其是針對群聊,離線消息特別多)。
等下次客戶端上線時,服務端會給客戶端強推全部的離線消息,導致客戶端卡死在登錄后的首頁。并且產品提出的需求,要擴大群成員的人數(由之前的百人群擴展到千人群、萬人群等)。
這樣一來,某些客戶端登錄后必定會因為大量離線消息而卡死,用戶體驗極為不好。
和客戶端的同事一起分析了一下原因:
- 1)用戶登錄,服務端通過循環分批下發所有離線消息,數據量較大;
- 2)客戶端登錄后進入首頁,需要加載的數據不光有離線消息,還有其他初始化數據;
- 3)不同價位的客戶端處理數據能力有限,處理聊天消息時,需要把消息存儲到本地數據庫,并且刷新UI界面,回復給服務端ack消息,整個過程很耗性能。
(慶幸的是,在線消息目前沒有性能問題)。
所以針對上述問題,結合產品對IM系統的遠大規劃,我們服務端決定優化離線消息(稍微吐槽一下,客戶端處理能力不夠,為什么要服務端做優化?服務端的性能遠沒達到瓶頸。。。)。
5、升級改造之路
值得慶幸的是,筆者100%參與這次系統優化的全部過程,包括技術選型、方案制定和最后的代碼編寫。在此期間,筆者思考出多種方案,然后和服務端、客戶端同事一起討論,最后定下來一套穩定的方案。
5.1 方案一(被pass掉的一個方案)
▶ 【問題癥狀】:
客戶端登錄卡頓的主要原因是,服務端會強推大量離線消息給客戶端,客戶端收到離線消息后會回復服務端ack,然后將消息存儲到本地數據庫、刷新UI等??蛻舳朔答仯词箍蛻舳瞬捎卯惒椒绞揭矔斜容^嚴重的性能問題。
▶ 【于是我想】:
為什么客戶端收到消息后還沒有將數據存儲到數據庫就回復給服務端ack?很有可能存儲失敗,這本身不合理,這是其一。其二,服務端強推導致客戶端卡死,不關心客戶端的處理能力,不合理。
▶ 【偽代碼如下】:
int max = 100;
//從新庫讀
while(max > 0) {
List<OfflineMsgInfo> offlineMsgListNew = shardChatOfflineMsgDao.getByToUid(uid, 20);
if(CollectionUtils.isEmpty(offlineMsgListNew)) {
break;
}
handleOfflineMsg(uid, offlineMsgListNew, checkOnlineWhenSendingOfflineMsg);
max--;
}
▶ 【初步方案】:
既然強推不合理,我們可以換一種方式,根據客戶端不同機型的處理能力的不同,服務端采用不同的速度下發。
我們可以把整個過程當成一種生產者消費者模型,服務端是消息生產者,客戶端是消息消費者??蛻舳耸盏较?,將消息存儲在本地數據庫,刷新UI界面后,再向服務端發送ack消息,服務端收到客戶端的ack消息后,再推送下一批消息。
這么一來,消息下發速度完全根據客戶端的處理能力,分批下發。但這種方式仍然屬于推方式。
▶ 【悲劇結果】:
然而,理想很豐滿,現實卻很骨感。
針對這個方案,客戶端提出一些問題:
- 1)雖然這種方案,客戶端不會卡死,但是如果當前用戶的離線消息特別多,那么收到所有離線消息的時間會非常長;
- 2)客戶端每次收到消息后會刷新界面,很有可能客戶端會發生,界面上下亂跳的畫面。
so,這個方案被否定了。。。
5.2 方案二
▶ 【我的思考】:
既然強推的數據量過大,我們是否可以做到,按需加載?客戶端需要讀取離線消息的時候服務端給客戶端下發,不需要的時候,服務端就不下發。
▶ 【技術方案】:針對離線消息,我們做了如下方案的優化
1)我們增加了離線消息計數器的概念:保存了每個用戶的每個會話,未讀的消息的元數據(包括未讀消息數,最近的一條未讀消息、時間戳等數據),這個計數器用于客戶端顯示未讀消息的的紅色氣泡。這個數據屬于增量數據,只保留離線期間收到的消息元數據。
消息格式如下:
{
"sessionId1":{
"count":20,
"lastMsg":[
"最后N條消息"
],
"timestamp":1234567890
},
"sessionId2":{
}
}
2)客戶端每次登錄時,服務端不推送全量離線消息,只推送離線消息計數器(這部分數據存儲在redis里,并且數據量很?。@個數量用戶顯示在客戶端消息列表的未讀消息小紅點上。
3)客戶端拿到這些離線消息計數器數據,遍歷會話列表,依次將未讀消息數量累加(注意:不是覆蓋,服務端保存客戶端離線后的增量數據),然后通知服務端清空離線消息計數器的增量數據。
4)當客戶端進入某會話后,上拉加載時,通過消息的msgId等信息發送HTTP請求給服務端,服務端再去分頁查詢離線消息返回給客戶端。
5)客戶端收到消息并保存在本地數據庫后,向服務端發送ack,然后服務端刪除離線消息表的離線消息。
▶ 【預期結果】:
客戶端、服務端的技術人員認可這個方案。我們通過推拉結合的方式,解決了客戶端加載離線消息卡頓的問題。(改造前是強推,改造后采用推拉結合的方式)
流程圖如下:
▶ 【新的問題】:
方案雖然通過了,但是引發了一個新問題:即客戶端消息銜接問題。
問題描述如下:客戶端登錄后進入會話頁面,因為客戶端本身就保存著歷史消息,那么客戶端下拉加載新消息時,到底怎么判斷要加載本地歷史消息?還是要請求服務端加載離線消息呢?
經過一番思考,服務端和客戶端最終達成了一致的方案:
- 1)在未讀消息計數器的小紅點邏輯中,服務端會把每個會話的最近N條消息一起下發給客戶端;
- 2)客戶端進入會話時,會根據未讀消息計數器的最近N條消息展示首頁數據;
- 3)客戶端每次下拉加載時,請求服務端,服務端按時間倒排離線消息返回當前會話最近一頁離線消息,直到離線消息庫中的數據全部返回給客戶端;
- 4)當離線消息庫中沒有離線消息后,返回給客戶端一個標識,客戶端根據這個標識,在會話頁面下一次下拉加載時不請求服務端的離線消息,直接請求本地數據庫。
6、消息ACK邏輯的優化
最后,我們也對消息ack的邏輯進行了優化。
優化前:服務端采用push模型給客戶端推消息,不論是在線消息還是離線消息,ack的邏輯都一樣,其中還用到了kafka、redis等中間件,流程很復雜(我在這里就不詳細展開介紹ack的具體流程了,反正不合理)。
離線消息和在線消息不同的是,我們不存儲在線消息,而離線消息會有一個單獨的庫存儲。完全沒必要用在線消息的ack邏輯去處理離線消息,反而很不合理,不僅流程上有問題,也浪費kafka、redis等中間件性能。
優化后:我們和客戶端決定在每次下拉加載離線消息時,將收到的上一批離線消息的msgId或消息偏移量等信息發送給服務端,服務端直接根據msgId刪除離線庫中已經發送給客戶端的離線消息,再返回給客戶端下一批離線消息。
另外:我們還增加了消息漫游功能,用戶切換手機登錄后仍然可以查到歷史消息,這部分內容我就不展開詳細介紹給大家了。
7、設計優化方案時的文檔截圖(僅供參考)
下面是優化的方案文檔截圖,請大家參考。
附錄:更多IM開發相關文章匯總
[1] IM開發熱門文章:
《新手入門一篇就夠:從零開發移動端IM》
《移動端IM開發者必讀(一):通俗易懂,理解移動網絡的“弱”和“慢”》
《移動端IM開發者必讀(二):史上最全移動弱網絡優化方法總結》
《從客戶端的角度來談談移動端IM的消息可靠性和送達機制》
《現代移動端網絡短連接的優化手段總結:請求速度、弱網適應、安全保障》
《騰訊技術分享:社交網絡圖片的帶寬壓縮技術演進之路》
《小白必讀:閑話HTTP短連接中的Session和Token》
《IM開發基礎知識補課:正確理解前置HTTP SSO單點登錄接口的原理》
《移動端IM中大規模群消息的推送如何保證效率、實時性?》
《移動端IM開發需要面對的技術問題》
《開發IM是自己設計協議用字節流好還是字符流好?》
《請問有人知道語音留言聊天的主流實現方式嗎?》
《IM消息送達保證機制實現(一):保證在線實時消息的可靠投遞》
《IM消息送達保證機制實現(二):保證離線消息的可靠投遞》
《如何保證IM實時消息的“時序性”與“一致性”?》
《一個低成本確保IM消息時序的方法探討》
《IM單聊和群聊中的在線狀態同步應該用“推”還是“拉”?》
《IM群聊消息如此復雜,如何保證不丟不重?》
《談談移動端 IM 開發中登錄請求的優化》
《移動端IM登錄時拉取數據如何作到省流量?》
《淺談移動端IM的多點登錄和消息漫游原理》
《完全自已開發的IM該如何設計“失敗重試”機制?》
《通俗易懂:基于集群的移動端IM接入層負載均衡方案分享》
《微信對網絡影響的技術試驗及分析(論文全文)》
《即時通訊系統的原理、技術和應用(技術論文)》
《開源IM工程“蘑菇街TeamTalk”的現狀:一場有始無終的開源秀》
《QQ音樂團隊分享:Android中的圖片壓縮技術詳解(上篇)》
《QQ音樂團隊分享:Android中的圖片壓縮技術詳解(下篇)》
《騰訊原創分享(一):如何大幅提升移動網絡下手機QQ的圖片傳輸速度和成功率》
《騰訊原創分享(二):如何大幅壓縮移動網絡下APP的流量消耗(上篇)》
《騰訊原創分享(三):如何大幅壓縮移動網絡下APP的流量消耗(下篇)》
《如約而至:微信自用的移動端IM網絡層跨平臺組件庫Mars已正式開源》
《基于社交網絡的Yelp是如何實現海量用戶圖片的無損壓縮的?》
《騰訊技術分享:騰訊是如何大幅降低帶寬和網絡流量的(圖片壓縮篇)》
《騰訊技術分享:騰訊是如何大幅降低帶寬和網絡流量的(音視頻技術篇)》
《字符編碼那點事:快速理解ASCII、Unicode、GBK和UTF-8》
《全面掌握移動端主流圖片格式的特點、性能、調優等》
《子彈短信光鮮的背后:網易云信首席架構師分享億級IM平臺的技術實踐》
《IM開發基礎知識補課(五):通俗易懂,正確理解并用好MQ消息隊列》
《微信技術分享:微信的海量IM聊天消息序列號生成實踐(算法原理篇)》
《自已開發IM有那么難嗎?手把手教你自擼一個Andriod版簡易IM (有源碼)》
《融云技術分享:解密融云IM產品的聊天消息ID生成策略》
《IM開發基礎知識補課(六):數據庫用NoSQL還是SQL?讀這篇就夠了!》
《適合新手:從零開發一個IM服務端(基于Netty,有完整源碼)》
《拿起鍵盤就是干:跟我一起徒手開發一套分布式IM系統》
《適合新手:手把手教你用Go快速搭建高性能、可擴展的IM系統(有源碼)》
《IM里“附近的人”功能實現原理是什么?如何高效率地實現它?》
《IM開發基礎知識補課(七):主流移動端賬號登錄方式的原理及設計思路》
《IM開發基礎知識補課(八):史上最通俗,徹底搞懂字符亂碼問題的本質》
《IM“掃一掃”功能很好做?看看微信“掃一掃識物”的完整技術實現》
《IM的掃碼登錄功能如何實現?一文搞懂主流應用的掃碼登錄技術原理》
《IM要做手機掃碼登錄?先看看微信的掃碼登錄功能技術原理》
《IM消息ID技術專題(一):微信的海量IM聊天消息序列號生成實踐(算法原理篇)》
《IM消息ID技術專題(二):微信的海量IM聊天消息序列號生成實踐(容災方案篇)》
《IM消息ID技術專題(三):解密融云IM產品的聊天消息ID生成策略》
《IM消息ID技術專題(四):深度解密美團的分布式ID生成算法》
《IM消息ID技術專題(五):開源分布式ID生成器UidGenerator的技術實現》
《IM開發寶典:史上最全,微信各種功能參數和邏輯規則資料匯總》
《IM開發干貨分享:我是如何解決大量離線聊天消息導致客戶端卡頓的》
>> 更多同類文章 ……
[2] IM群聊相關的技術文章:
《快速裂變:見證微信強大后臺架構從0到1的演進歷程(一)》
《如何保證IM實時消息的“時序性”與“一致性”?》
《IM單聊和群聊中的在線狀態同步應該用“推”還是“拉”?》
《IM群聊消息如此復雜,如何保證不丟不重?》
《微信后臺團隊:微信后臺異步消息隊列的優化升級實踐分享》
《移動端IM中大規模群消息的推送如何保證效率、實時性?》
《現代IM系統中聊天消息的同步和存儲方案探討》
《關于IM即時通訊群聊消息的亂序問題討論》
《IM群聊消息的已讀回執功能該怎么實現?》
《IM群聊消息究竟是存1份(即擴散讀)還是存多份(即擴散寫)?》
《一套高可用、易伸縮、高并發的IM群聊、單聊架構方案設計實踐》
《[技術腦洞] 如果把14億中國人拉到一個微信群里技術上能實現嗎?》
《IM群聊機制,除了循環去發消息還有什么方式?如何優化?》
《網易云信技術分享:IM中的萬人群聊技術方案實踐總結》
《阿里釘釘技術分享:企業級IM王者——釘釘在后端架構上的過人之處》
>> 更多同類文章 ……
[3] 有關IM架構設計的文章:
《淺談IM系統的架構設計》
《簡述移動端IM開發的那些坑:架構設計、通信協議和客戶端》
《一套海量在線用戶的移動端IM架構設計實踐分享(含詳細圖文)》
《一套原創分布式即時通訊(IM)系統理論架構方案》
《從零到卓越:京東客服即時通訊系統的技術架構演進歷程》
《蘑菇街即時通訊/IM服務器開發之架構選擇》
《騰訊QQ1.4億在線用戶的技術挑戰和架構演進之路PPT》
《微信后臺基于時間序的海量數據冷熱分級架構設計實踐》
《微信技術總監談架構:微信之道——大道至簡(演講全文)》
《如何解讀《微信技術總監談架構:微信之道——大道至簡》》
《快速裂變:見證微信強大后臺架構從0到1的演進歷程(一)》
《17年的實踐:騰訊海量產品的技術方法論》
《移動端IM中大規模群消息的推送如何保證效率、實時性?》
《現代IM系統中聊天消息的同步和存儲方案探討》
《IM開發基礎知識補課(二):如何設計大量圖片文件的服務端存儲架構?》
《IM開發基礎知識補課(三):快速理解服務端數據庫讀寫分離原理及實踐建議》
《IM開發基礎知識補課(四):正確理解HTTP短連接中的Cookie、Session和Token》
《WhatsApp技術實踐分享:32人工程團隊創造的技術神話》
《微信朋友圈千億訪問量背后的技術挑戰和實踐總結》
《王者榮耀2億用戶量的背后:產品定位、技術架構、網絡方案等》
《IM系統的MQ消息中間件選型:Kafka還是RabbitMQ?》
《騰訊資深架構師干貨總結:一文讀懂大型分布式系統設計的方方面面》
《以微博類應用場景為例,總結海量社交系統的架構設計步驟》
《快速理解高性能HTTP服務端的負載均衡技術原理》
《子彈短信光鮮的背后:網易云信首席架構師分享億級IM平臺的技術實踐》
《知乎技術分享:從單機到2000萬QPS并發的Redis高性能緩存實踐之路》
《IM開發基礎知識補課(五):通俗易懂,正確理解并用好MQ消息隊列》
《微信技術分享:微信的海量IM聊天消息序列號生成實踐(算法原理篇)》
《微信技術分享:微信的海量IM聊天消息序列號生成實踐(容災方案篇)》
《新手入門:零基礎理解大型分布式架構的演進歷史、技術原理、最佳實踐》
《一套高可用、易伸縮、高并發的IM群聊、單聊架構方案設計實踐》
《阿里技術分享:深度揭秘阿里數據庫技術方案的10年變遷史》
《阿里技術分享:阿里自研金融級數據庫OceanBase的艱辛成長之路》
《社交軟件紅包技術解密(一):全面解密QQ紅包技術方案——架構、技術實現等》
《社交軟件紅包技術解密(二):解密微信搖一搖紅包從0到1的技術演進》
《社交軟件紅包技術解密(三):微信搖一搖紅包雨背后的技術細節》
《社交軟件紅包技術解密(四):微信紅包系統是如何應對高并發的》
《社交軟件紅包技術解密(五):微信紅包系統是如何實現高可用性的》
《社交軟件紅包技術解密(六):微信紅包系統的存儲層架構演進實踐》
《社交軟件紅包技術解密(七):支付寶紅包的海量高并發技術實踐》
《社交軟件紅包技術解密(八):全面解密微博紅包技術方案》
《社交軟件紅包技術解密(九):談談手Q紅包的功能邏輯、容災、運維、架構等》
《社交軟件紅包技術解密(十):手Q客戶端針對2020年春節紅包的技術實踐》
《即時通訊新手入門:一文讀懂什么是Nginx?它能否實現IM的負載均衡?》
《即時通訊新手入門:快速理解RPC技術——基本概念、原理和用途》
《多維度對比5款主流分布式MQ消息隊列,媽媽再也不擔心我的技術選型了》
《從游擊隊到正規軍(一):馬蜂窩旅游網的IM系統架構演進之路》
《從游擊隊到正規軍(二):馬蜂窩旅游網的IM客戶端架構演進和實踐總結》
《IM開發基礎知識補課(六):數據庫用NoSQL還是SQL?讀這篇就夠了!》
《瓜子IM智能客服系統的數據架構設計(整理自現場演講,有配套PPT)》
《阿里釘釘技術分享:企業級IM王者——釘釘在后端架構上的過人之處》
《從游擊隊到正規軍(三):基于Go的馬蜂窩旅游網分布式IM系統技術實踐》
《微信后臺基于時間序的新一代海量數據存儲架構的設計實踐》
《IM開發基礎知識補課(九):想開發IM集群?先搞懂什么是RPC!》
>> 更多同類文章 ……
(本文同步發布于:http://www.52im.net/thread-3036-1-1.html)