Jack Jiang

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

          本文由轉(zhuǎn)轉(zhuǎn) 梁會彬、杜云杰分享,原題“轉(zhuǎn)轉(zhuǎn)IM的實踐與思考”,下文進行了排版和內(nèi)容優(yōu)化。

          1、引言

          接上篇《整體架構(gòu)設(shè)計》,筆者將以轉(zhuǎn)轉(zhuǎn)IM架構(gòu)為起點,介紹IM相關(guān)組件以及組件間的關(guān)系;以IM登陸和發(fā)消息的數(shù)據(jù)流轉(zhuǎn)為跑道,介紹IM靜態(tài)數(shù)據(jù)結(jié)構(gòu)、登陸和發(fā)消息時的動態(tài)數(shù)據(jù)變化;以IM常見問題為風(fēng)景,介紹保證IM實時性、可靠性、一致性的一般方案;以高可用、高并發(fā)為終點,介紹保證IM系統(tǒng)穩(wěn)定及性能的小技巧

           技術(shù)交流:

          2、系列文章

          本文是系列文章中的第2篇,本系列文章的大綱如下:

          3、本文作者

          梁會彬:轉(zhuǎn)轉(zhuǎn)架構(gòu)部資深Java工程師,主要負(fù)責(zé)服務(wù)治理平臺、Docker云平臺、IM、分布式ID生成器、短域名服務(wù)等,有豐富的線上實戰(zhàn)經(jīng)驗。

          4、 IM架構(gòu)回顧

          應(yīng)用層:使用IM服務(wù)的上游業(yè)務(wù)方,包括app(ios和android)、小程序/PC/m頁、push、業(yè)務(wù)方等。

          接入層:

          • 1)tcp entry:使用TCP協(xié)議,主要用于長連接保持、會話管理、協(xié)議解析;
          • 2)http entry:使用http協(xié)議,采用long pull技術(shù),主要用于長連接保持、會話管理、協(xié)議解析;
          • 3)mq:接收電商推廣等系統(tǒng)消息。推送量具有脈沖特點,使用mq削峰填谷;
          • 4)rpc-server:業(yè)務(wù)查詢用戶聊天數(shù)據(jù)、發(fā)送實時系統(tǒng)消息等。

          邏輯層:

          • 1)logic:核心邏輯服務(wù),負(fù)責(zé)登陸信息管理、在線消息管理、離線消息管理、在線推送管理等;
          • 2)ext-logic:擴展邏輯服務(wù),負(fù)責(zé)子母賬號推送、登陸信息統(tǒng)計、系統(tǒng)消息管理等。

          數(shù)據(jù)層:

          • 1)MySQL:聯(lián)系人數(shù)據(jù)、消息數(shù)據(jù)、系統(tǒng)消息數(shù)據(jù)等;
          • 2)Redis:登陸信息等。

          5、IM消息收發(fā)

          5.1場景說明

          數(shù)據(jù)流中以用戶A和用戶B的對話為例,其中用戶A的uid為1,用戶B的uid為2。

          下圖為用戶聊天場景圖:

          下圖為用戶聊天IM系統(tǒng)的數(shù)據(jù)流轉(zhuǎn)圖:

          5.2數(shù)據(jù)結(jié)構(gòu)

          登陸信息存儲在Redis中,聯(lián)系人和消息數(shù)據(jù)放在TiDB中。

          1)登陸信息:

          key:uid

          value:{entryIp:"127.0.0.1",entryPort:5000,loginTime:23443233}

          2)聯(lián)系人:

          說明:

          • 1)recent_msg_content:最近一條對話消息的內(nèi)容,用于聯(lián)系人列表中展示最近的消息內(nèi)容;
          • 2)recent_read_time:最近一次讀取該會話消息的時間,用于控制已讀狀態(tài),小于該時間的所有消息,都為已讀狀態(tài)。

          3)消息:

          說明:

          • 1)client_msg_id:客戶端生成的id,客戶端冪等設(shè)計,防重復(fù);
          • 2)direction:消息方向(0代表較大uid向較小uid發(fā)送消息,1則反之)。

          數(shù)據(jù)流=數(shù)據(jù)+流。上面部分講數(shù)據(jù),即聯(lián)系人和消息表,從靜態(tài)的角度介紹了IM的數(shù)據(jù)結(jié)構(gòu);下面部分講流(IM中最重要的兩個流程),即登陸和發(fā)消息,從動態(tài)的角度來闡述IM系統(tǒng)中數(shù)據(jù)的流轉(zhuǎn)。

          5.3主要流程

          5.3.1 )登陸:

          1)問題:entry地址發(fā)現(xiàn):app直接訪問vip,由vip轉(zhuǎn)發(fā)到entry。

          2)流程(下面的數(shù)字為圖中數(shù)字的說明):

          • 1)建連:app通過vip發(fā)起與entry連接;
          • 2)轉(zhuǎn)發(fā):entry轉(zhuǎn)發(fā)登陸信息到logic,獲取用戶uid并管理該用戶的連接;
          • 3)入庫:logic記錄用戶登陸信息到redis。

          3)數(shù)據(jù):

          Redis中數(shù)據(jù)如下:

          key:1

          value:{entryIp:"127.0.0.1",entryPort:5000,loginTime:23443233}

          5.3.2 )發(fā)消息(下面的數(shù)字為圖二中數(shù)字的說明):

          1)流程處理:

          • 1)發(fā)送:通過用戶與entry的長連接發(fā)送文字"hello world";
          • 2)轉(zhuǎn)發(fā):entry轉(zhuǎn)發(fā)文字信息"hello world"到logic;
          • 3)入庫:logic存入數(shù)據(jù)庫,即更新聯(lián)系人表和消息表,其中聯(lián)系人表更新recent_msg_content字段,消息表增加一條新消息記錄;
          • 4)推送:從Redis中獲取用戶B登陸entry,如果未登錄,走離線邏輯(發(fā)送push、推送微信、短信喚起);
          • 5)送達:用戶B收到消息;
          • 6)確認(rèn):發(fā)送ack到entry;
          • 7)完成:logic收到ack,取消定時器;如果沒有收到ack,logic會定時重發(fā)(用戶在線時)。

          2)數(shù)據(jù):

          聯(lián)系人數(shù)據(jù)如下:

          消息表數(shù)據(jù)如下:

          5.3.3)關(guān)于數(shù)據(jù)的幾個問題:

          1)消息和聯(lián)系人是如何分庫分表的?使用TiDB,無需分庫分表(現(xiàn)在的表設(shè)計支持根據(jù)uid_a分表,也就是無縫支持以MySQL為存儲)。

          2)聯(lián)系人表一條消息為什么記錄了兩條數(shù)據(jù)?業(yè)務(wù)邏輯上,考量支持已讀、刪除聯(lián)系人;索引性能上,考慮用戶查詢聯(lián)系人時,sql條件為where uid_a=?,聯(lián)系人表索引為uid_a,如果存單條數(shù)據(jù),無法有效利用索引。

          3)消息表一條消息記錄一條數(shù)據(jù),用戶B與用戶A的消息怎么查詢?該表索引為<big_uid, small_uid>聯(lián)合索引,無論是用戶A查詢與用戶B的聊天信息,還是用戶B查詢用戶A的聊天信息,其sql統(tǒng)統(tǒng)為where big_uid =max(uid_a,uid_b) and small_uid =min(uid_a,uid_b),然后根據(jù)direction字段展示聊天方向,這樣就可以用一條消息,無需和聯(lián)系人表一樣存儲兩份數(shù)據(jù),滿足兩種查詢,節(jié)省一半的消息存儲。

          6、IM常見問題

          6.1消息的實時性

          1)是什么:

          用戶A給用戶B發(fā)送消息"hello world",用戶B怎么第一時間感知到?這里說的實時性,就是指用戶如何實時獲取發(fā)送的消息。

          2)io模型帶來的啟示:

          • 1)poll、select、epoll;
          • 2)poll/select相比epoll最大的劣勢在于輪詢,輪詢就需要輪詢間隔,間隔小會浪費cpu,間隔大會不實時。epoll具有don't call me i will call you的特點,保證實時性;
          • 3)IM也面臨著輪詢還是通知的問題,也就是pull和push的問題。

          3)怎么辦:

          • 1)向epoll致敬:epoll_create、epoll_ctl、epoll_wait(此三者是epoll系統(tǒng)調(diào)用api);
          • 2)整個IM系統(tǒng)和epoll模型類似,app和entry保持長連接(epoll_create);entry session管理(即長連接管理epoll_ctl);logic等待用戶A發(fā)送給用戶B消息,獲取用戶B所登陸entry,觸發(fā)推送消息(epoll_wait);綜述,entry扮演著(epoll_create,epoll_ctl),logic扮演著(epoll_wait)這樣IM系統(tǒng)就解決了消息實時性問題。

          6.2消息的可靠性

          1)是什么:

          • 1)用戶A給用戶B發(fā)送消息"hello world",用戶B在線,怎么保證用戶B確實收到了消息。這里說的可靠性,就是指用戶如何可靠發(fā)送的消息。

          2)tcp模型帶來的啟示:

          • 1)失敗重傳、ack確認(rèn)。

          3)怎么辦:

          • 1)失敗重傳:圖二中(1、發(fā)送2、轉(zhuǎn)發(fā)3、入庫)失敗,告知客戶端失敗,由客戶端重傳;
          • 2)ack確認(rèn):圖二中(4、推送5、送達6、確認(rèn)7、完成)失敗,即ack處理失敗,啟動重新通知邏輯。

          6.3消息的一致性

          1)是什么:

          • 1)現(xiàn)象:本來用戶A給用戶B發(fā)送了一個"hello world",而用戶B確收到了兩個"hello world";
          • 2)原因:由于可靠性邏輯中的重傳邏輯,可能造成客戶端認(rèn)為失敗了,但是服務(wù)端卻成功了;推送ack返回錯誤,造成重推。

          2)身份證帶來的啟示。

          3)怎么辦:

          • 1)client_msg_id:客戶端發(fā)送消息時生成客戶端id,對于單個客戶端,該id具有唯一性,像身份證一樣;
          • 2)客戶端去重:如果客戶端發(fā)現(xiàn)相同client_msg_id的消息,則僅僅展示一條數(shù)據(jù)。

          7、IM高可用、高并發(fā)

          1)擴縮容:

          依托公司rpc服務(wù)注冊發(fā)現(xiàn)能力,借助docker快速擴容,核心處理邏輯logic服務(wù)實現(xiàn)秒級擴容。擴容依據(jù)為各種監(jiān)控指標(biāo),包括機器性能指標(biāo)、 entry/logic qps指標(biāo)、jvm指標(biāo)、sql監(jiān)控等綜合考量。

          2)熔斷:

          當(dāng)大流量進入時,如果核心服務(wù)依賴的服務(wù)(比如母子賬號服務(wù))出現(xiàn)不可用的情況。這時,我們是直接使IM服務(wù)不可用嗎?是不是有更好的選擇?答案是肯定的,我們可以犧牲母子賬號功能,也就是熔斷不重要的依賴服務(wù),做到柔性可用。

          3)限流:

          如果遇到瞬時高流量,僅僅擴容有可能適得其反。如果db處理能力達到極限,擴容就不是明智的選擇,擴容反而會導(dǎo)致db連接增多,增加db的壓力,導(dǎo)致服務(wù)崩潰。這時退一步采用限流,應(yīng)用“fast fail”策略,讓部分流量快速失敗,減小服務(wù)壓力,達到部分可用的效果。

          4)總結(jié):

          IM作為電商應(yīng)用中的一個重要節(jié)點,其重要性不言而喻,對其怎么重視都不為過。我們使用監(jiān)控工具定義IM的核心metrics,根據(jù)指標(biāo)進行擴縮容,這樣做到了高可用;

          高可用是萬能的嗎?IM依賴了很多服務(wù),比如用戶,母子賬號,風(fēng)控等服務(wù),如果這些服務(wù)出現(xiàn)不可用的情況呢?這個時候就要學(xué)習(xí)一下古人的智慧,壯士斷腕,犧牲小我,換取大我了,也就是柔性可用;

          僅僅這樣還是不夠的,如果遇到突發(fā)流量,db(不可瞬時擴大處理能力)等處理能力達到極限時這個時候就要犧牲部分請求了,也就要做到部分可用。從“高可用”到“柔性可用”再到“部分可用”,面對不同case,IM要做到游刃有余。

          其實,這種思想又何止IM呢,任何重要的服務(wù)都要面對這些問題吧,推而廣之,面對自己負(fù)責(zé)的服務(wù),怎么精細(xì)小心都不為過。

          8、本文小結(jié)

          誠然,這篇文章給大家對IM系統(tǒng)簡單的認(rèn)識,闡述了IM的一般架構(gòu)、主要業(yè)務(wù)邏輯、常見問題和解決方案以及服務(wù)治理相關(guān)應(yīng)用,IM還有很多業(yè)務(wù)邏輯和技術(shù)挑戰(zhàn)。

          在業(yè)務(wù)上,如未讀數(shù)、群聊、多端登陸、母子賬號等;在技術(shù)上,entry長連接100k問題優(yōu)化、時間輪計時器實現(xiàn)、海量數(shù)據(jù)拆分與存儲選型等。

          路漫漫其修遠兮,吾將上下而求索。

          9、參考資料

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

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

          [3] 零基礎(chǔ)IM開發(fā)入門(四):什么是IM系統(tǒng)的消息時序一致性?

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

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

          [6] 如何保證IM實時消息的“時序性”與“一致性”?

          [7] 阿里IM技術(shù)分享(四):閑魚億級IM消息系統(tǒng)的可靠投遞優(yōu)化實踐

          [8] 阿里IM技術(shù)分享(五):閑魚億級IM消息系統(tǒng)的及時性優(yōu)化實踐

          [9] 一套億級用戶的IM架構(gòu)技術(shù)干貨(下篇):可靠性、有序性、弱網(wǎng)優(yōu)化等

          [10] 融云技術(shù)分享:全面揭秘億級IM消息的可靠投遞機制

          [11] 一套海量在線用戶的移動端IM架構(gòu)設(shè)計實踐分享(含詳細(xì)圖文)

          [12] 一套原創(chuàng)分布式即時通訊(IM)系統(tǒng)理論架構(gòu)方案

          [13] 從零到卓越:京東客服即時通訊系統(tǒng)的技術(shù)架構(gòu)演進歷程

          [14] 蘑菇街即時通訊/IM服務(wù)器開發(fā)之架構(gòu)選擇

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

          [16] 一套高可用、易伸縮、高并發(fā)的IM群聊、單聊架構(gòu)方案設(shè)計實踐

          [17] 馬蜂窩旅游網(wǎng)的IM系統(tǒng)架構(gòu)演進之路

          [18] 一套分布式IM即時通訊系統(tǒng)的技術(shù)選型和架構(gòu)設(shè)計

          [19] 微信團隊分享:來看看微信十年前的IM消息收發(fā)架構(gòu),你做到了嗎

          [20] 攜程技術(shù)分享:億級流量的辦公IM及開放平臺技術(shù)實踐


          (本文已同步發(fā)布于:http://www.52im.net/thread-4773-1-1.html)



          作者:Jack Jiang (點擊作者姓名進入Github)
          出處:http://www.52im.net/space-uid-1.html
          交流:歡迎加入即時通訊開發(fā)交流群 215891622
          討論:http://www.52im.net/
          Jack Jiang同時是【原創(chuàng)Java Swing外觀工程BeautyEye】【輕量級移動端即時通訊框架MobileIMSDK】的作者,可前往下載交流。
          本博文 歡迎轉(zhuǎn)載,轉(zhuǎn)載請注明出處(也可前往 我的52im.net 找到我)。


          只有注冊用戶登錄后才能發(fā)表評論。


          網(wǎng)站導(dǎo)航:
           
          Jack Jiang的 Mail: jb2011@163.com, 聯(lián)系QQ: 413980957, 微信: hellojackjiang
          主站蜘蛛池模板: 萨嘎县| 南郑县| 加查县| 同仁县| 古浪县| 雅安市| 时尚| 邵阳县| 余姚市| 岚皋县| 南京市| 五莲县| 吴江市| 洛扎县| 陇西县| 峨边| 临夏县| 藁城市| 东辽县| 延吉市| 建宁县| 东明县| 濉溪县| 永济市| 静宁县| 天祝| 汕头市| 滁州市| 甘德县| 丰顺县| 黎城县| 称多县| 连云港市| 阿尔山市| 纳雍县| 和龙市| 利川市| 静安区| 仙桃市| 会东县| 和平区|