轉轉平台IM系統架構設計與實踐(二):詳細設計與實現
本文由轉轉 梁會彬、杜雲傑分享,原題“轉轉IM的實踐與思考”,下文進行了排版和内容優化。
1、引言
接上篇《整體架構設計》,筆者将以轉轉IM架構為起點,介紹IM相關組件以及組件間的關系;以IM登陸和發消息的數據流轉為跑道,介紹IM靜态數據結構、登陸和發消息時的動态數據變化;以IM常見問題為風景,介紹保證IM實時性、可靠性、一緻性的一般方案;以高可用、高并發為終點,介紹保證IM系統穩定及性能的小技巧。
2、系列文章
本文是系列文章中的第2篇,本系列文章的大綱如下:
轉轉平台IM系統架構設計與實踐(二):詳細設計與實現(* 本文)
3、本文作者
梁會彬:轉轉架構部資深Java工程師,主要負責服務治理平台、Docker雲平台、IM、分布式ID生成器、短域名服務等,有豐富的線上實戰經驗。
4、 IM架構回顧
應用層:使用IM服務的上遊業務方,包括app(ios和android)、小程序/PC/m頁、push、業務方等。
接入層:
1)tcp entry:使用TCP協議,主要用于長連接保持、會話管理、協議解析;
2)http entry:使用http協議,采用long pull技術,主要用于長連接保持、會話管理、協議解析;
3)mq:接收電商推廣等系統消息。推送量具有脈沖特點,使用mq削峰填谷;
4)rpc-server:業務查詢用戶聊天數據、發送實時系統消息等。
邏輯層:
1)logic:核心邏輯服務,負責登陸信息管理、在線消息管理、離線消息管理、在線推送管理等;
2)ext-logic:擴展邏輯服務,負責子母賬号推送、登陸信息統計、系統消息管理等。
數據層:
1)MySQL:聯系人數據、消息數據、系統消息數據等;
2)Redis:登陸信息等。
5、IM消息收發5.1場景說明
數據流中以用戶A和用戶B的對話為例,其中用戶A的uid為1,用戶B的uid為2。
下圖為用戶聊天場景圖:
下圖為用戶聊天IM系統的數據流轉圖:
5.2數據結構
登陸信息存儲在Redis中,聯系人和消息數據放在TiDB中。
1)登陸信息:
key:uid
value:{entryIp:"127.0.0.1",entryPort:5000,loginTime:23443233}
2)聯系人:
說明:
1)recent_msg_content:最近一條對話消息的内容,用于聯系人列表中展示最近的消息内容;
2)recent_read_time:最近一次讀取該會話消息的時間,用于控制已讀狀态,小于該時間的所有消息,都為已讀狀态。
3)消息:
說明:
1)client_msg_id:客戶端生成的id,客戶端幂等設計,防重複;
2)direction:消息方向(0代表較大uid向較小uid發送消息,1則反之)。
數據流=數據+流。上面部分講數據,即聯系人和消息表,從靜态的角度介紹了IM的數據結構;下面部分講流(IM中最重要的兩個流程),即登陸和發消息,從動态的角度來闡述IM系統中數據的流轉。
5.3主要流程5.3.1 )登陸:
1)問題:entry地址發現:app直接訪問vip,由vip轉發到entry。
2)流程(下面的數字為圖中數字的說明):
1)建連:app通過vip發起與entry連接;
2)轉發:entry轉發登陸信息到logic,獲取用戶uid并管理該用戶的連接;
3)入庫:logic記錄用戶登陸信息到redis。
3)數據:
Redis中數據如下:
key:1
value:{entryIp:"127.0.0.1",entryPort:5000,loginTime:23443233}
5.3.2 )發消息(下面的數字為圖二中數字的說明):
1)流程處理:
1)發送:通過用戶與entry的長連接發送文字"hello world";
2)轉發:entry轉發文字信息"hello world"到logic;
3)入庫:logic存入數據庫,即更新聯系人表和消息表,其中聯系人表更新recent_msg_content字段,消息表增加一條新消息記錄;
4)推送:從Redis中獲取用戶B登陸entry,如果未登錄,走離線邏輯(發送push、推送微信、短信喚起);
5)送達:用戶B收到消息;
6)确認:發送ack到entry;
7)完成:logic收到ack,取消定時器;如果沒有收到ack,logic會定時重發(用戶在線時)。
2)數據:
聯系人數據如下:
消息表數據如下:
5.3.3)關于數據的幾個問題:
1)消息和聯系人是如何分庫分表的?使用TiDB,無需分庫分表(現在的表設計支持根據uid_a分表,也就是無縫支持以MySQL為存儲)。
2)聯系人表一條消息為什麼記錄了兩條數據?業務邏輯上,考量支持已讀、删除聯系人;索引性能上,考慮用戶查詢聯系人時,sql條件為where uid_a=?,聯系人表索引為uid_a,如果存單條數據,無法有效利用索引。
3)消息表一條消息記錄一條數據,用戶B與用戶A的消息怎麼查詢?該表索引為<big_uid, small_uid>聯合索引,無論是用戶A查詢與用戶B的聊天信息,還是用戶B查詢用戶A的聊天信息,其sql統統為where big_uid =max(uid_a,uid_b) and small_uid =min(uid_a,uid_b),然後根據direction字段展示聊天方向,這樣就可以用一條消息,無需和聯系人表一樣存儲兩份數據,滿足兩種查詢,節省一半的消息存儲。
6、IM常見問題6.1消息的實時性
1)是什麼:
用戶A給用戶B發送消息"hello world",用戶B怎麼第一時間感知到?這裡說的實時性,就是指用戶如何實時獲取發送的消息。
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系統調用api);
2)整個IM系統和epoll模型類似,app和entry保持長連接(epoll_create);entry session管理(即長連接管理epoll_ctl);logic等待用戶A發送給用戶B消息,獲取用戶B所登陸entry,觸發推送消息(epoll_wait);綜述,entry扮演着(epoll_create,epoll_ctl),logic扮演着(epoll_wait)這樣IM系統就解決了消息實時性問題。
6.2消息的可靠性
1)是什麼:
1)用戶A給用戶B發送消息"hello world",用戶B在線,怎麼保證用戶B确實收到了消息。這裡說的可靠性,就是指用戶如何可靠發送的消息。
2)tcp模型帶來的啟示:
1)失敗重傳、ack确認。
3)怎麼辦:
1)失敗重傳:圖二中(1、發送2、轉發3、入庫)失敗,告知客戶端失敗,由客戶端重傳;
2)ack确認:圖二中(4、推送5、送達6、确認7、完成)失敗,即ack處理失敗,啟動重新通知邏輯。
6.3消息的一緻性
1)是什麼:
1)現象:本來用戶A給用戶B發送了一個"hello world",而用戶B确收到了兩個"hello world";
2)原因:由于可靠性邏輯中的重傳邏輯,可能造成客戶端認為失敗了,但是服務端卻成功了;推送ack返回錯誤,造成重推。
2)身份證帶來的啟示。
3)怎麼辦:
1)client_msg_id:客戶端發送消息時生成客戶端id,對于單個客戶端,該id具有唯一性,像身份證一樣;
2)客戶端去重:如果客戶端發現相同client_msg_id的消息,則僅僅展示一條數據。
7、IM高可用、高并發
1)擴縮容:
依托公司rpc服務注冊發現能力,借助docker快速擴容,核心處理邏輯logic服務實現秒級擴容。擴容依據為各種監控指标,包括機器性能指标、 entry/logic qps指标、jvm指标、sql監控等綜合考量。
2)熔斷:
當大流量進入時,如果核心服務依賴的服務(比如母子賬号服務)出現不可用的情況。這時,我們是直接使IM服務不可用嗎?是不是有更好的選擇?答案是肯定的,我們可以犧牲母子賬号功能,也就是熔斷不重要的依賴服務,做到柔性可用。
3)限流:
如果遇到瞬時高流量,僅僅擴容有可能适得其反。如果db處理能力達到極限,擴容就不是明智的選擇,擴容反而會導緻db連接增多,增加db的壓力,導緻服務崩潰。這時退一步采用限流,應用“fast fail”策略,讓部分流量快速失敗,減小服務壓力,達到部分可用的效果。
4)總結:
IM作為電商應用中的一個重要節點,其重要性不言而喻,對其怎麼重視都不為過。我們使用監控工具定義IM的核心metrics,根據指标進行擴縮容,這樣做到了高可用;
高可用是萬能的嗎?IM依賴了很多服務,比如用戶,母子賬号,風控等服務,如果這些服務出現不可用的情況呢?這個時候就要學習一下古人的智慧,壯士斷腕,犧牲小我,換取大我了,也就是柔性可用;
僅僅這樣還是不夠的,如果遇到突發流量,db(不可瞬時擴大處理能力)等處理能力達到極限時這個時候就要犧牲部分請求了,也就要做到部分可用。從“高可用”到“柔性可用”再到“部分可用”,面對不同case,IM要做到遊刃有餘。
其實,這種思想又何止IM呢,任何重要的服務都要面對這些問題吧,推而廣之,面對自己負責的服務,怎麼精細小心都不為過。
8、本文小結
誠然,這篇文章給大家對IM系統簡單的認識,闡述了IM的一般架構、主要業務邏輯、常見問題和解決方案以及服務治理相關應用,IM還有很多業務邏輯和技術挑戰。
在業務上,如未讀數、群聊、多端登陸、母子賬号等;在技術上,entry長連接100k問題優化、時間輪計時器實現、海量數據拆分與存儲選型等。
路漫漫其修遠兮,吾将上下而求索。
9、參考資料
[3] 零基礎IM開發入門(四):什麼是IM系統的消息時序一緻性?
[4] IM消息送達保證機制實現(一):保證在線實時消息的可靠投遞
[5] IM消息送達保證機制實現(二):保證離線消息的可靠投遞
[7] 阿裡IM技術分享(四):閑魚億級IM消息系統的可靠投遞優化實踐
[8] 阿裡IM技術分享(五):閑魚億級IM消息系統的及時性優化實踐
[9] 一套億級用戶的IM架構技術幹貨(下篇):可靠性、有序性、弱網優化等
[11] 一套海量在線用戶的移動端IM架構設計實踐分享(含詳細圖文)
[13] 從零到卓越:京東客服即時通訊系統的技術架構演進曆程
[14] 蘑菇街即時通訊/IM服務器開發之架構選擇
[16] 一套高可用、易伸縮、高并發的IM群聊、單聊架構方案設計實踐
[17] 馬蜂窩旅遊網的IM系統架構演進之路
[19] 微信團隊分享:來看看微信十年前的IM消息收發架構,你做到了嗎
- 上一篇 七律.鬧元宵
- 下一篇 多措并舉集聚 縣域人才強磁場
添加新評論