一个海量在线用户即时通讯系统(IM)的完整设计
CSDN博客有圖片大小限制,有些圖片無法顯示,可查看微信公眾號中原文。
1 服務器端設計
1.1 總體架構
總體架構包括5個層級,具體內容如下圖。
1.1.1 用戶端
移動端重點是移動端,支持IOS/Android系統,包括IM App,嵌入消息功能的瓜子App,未來還可能接入客服系統。
1.1.2 用戶端API
針對TCP協議,提供IOS/Android開發SDK。對于H5頁面,提供WebSocket接口
1.1.3 接入層
接入層主要任務是保持海量用戶連接(接入)、攻擊防護、將海量連接整流成少量TCP連接與邏輯層通訊。
1.1.4 邏輯層
邏輯層負責IM系統各項功能的核心邏輯實現。包括單聊(c2c)、上報(c2s)、推送(s2c)、群聊(c2g)、離線消息、登錄授權、組織機構樹等等內容。
1.1.5 存儲層
存儲層負責緩存或存儲IM系統相關數據,主要包括用戶狀態及路由(緩存),消息數據(MySQL也可采用NoSql,如MangoDB),文件數據(文件服務器)。
1.2 邏輯結構
1.2.1 核心結構
核心結構部分描述IM系統核心組件及其關系。結構圖如下。
客戶端從Iplist服務獲取接入層IP地址(也可采用域名的方式解析得到接入層IP地址),建立與接入層的連接(可能為短連接),從而實現客戶端與IM服務器的數據交互;業務線服務器可以通過服務器端API建立與IM服務器的聯系,向客戶端推送消息;客戶端上報到業務服務器的消息,IM服務器會通過mq投遞給業務服務器。
1.2.2 tcp接入核心流程
1.2.2.1 登錄授權(auth)
1、客戶端通過統一登錄系統實現登錄,得到token。
2、客戶端用uid和token向msg-gate發起授權驗證請求。
3、msg-gate同步調用msg-logic的驗證接口
4、msg-logic請求sso系統驗證token合法性
5、msg-gate得到登錄結果后,設置session狀態,并向客戶端返回授權結果。
1.2.2.2 登出(logout)
1、客戶端發起logout請求,msg-gate設置對應Peer為未登錄狀態。
2、Msg-gate給客戶端一個ack響應。
3、Msg-gate通知msg-logic用戶登出。
1.2.2.3踢人(kickout)
用戶請求授權時,可能在另一個設備(同類型設備)開著軟件處于登錄狀態。這種情況需要系統將那個設備踢下線。
1-5步,參看Auth流程。
6、Logic檢索Redis,查看是否該用戶在其他地方登錄。
7、如果在其他地方登錄,發起kickout命令。(如果沒有登錄,整個流程結束)
8、Gate向用戶發起kickout請求,并在短時間內(確保客戶端收到kickout數據)關閉socket連接。
1.2.2.4 上報(c2s)
?
1、客戶端向gate發送數據
2、Gate回一個ack包,向客戶端確認已經收到數據
3、Gate將數據包傳遞給logic
4、Logic根據數據投遞目的地,選擇對應的mq隊列進行投遞
5、業務服務器得到數據
1.2.2.5推送(s2c)
1、業務線調用push數據接口sendMsg
2、Logic向redis檢索目標用戶狀態。如果目標用戶不在線,丟棄數據(未來可根據業務場景定制化邏輯);如果用戶在線,查詢到用戶連接的接入層gate
3、Logic向用戶所在的gate發送數據
4、Gate向用戶推送數據。(如果用戶不在線,通知logic用戶不在線)
5、客戶端收到數據后向gate發送ack反饋
6、Gate將ack信息傳遞給logic層,用于其他可能的邏輯處理(如日志,確認送達等)
1.2.2.6單對單聊天(c2c)
1、App1向gate1發送信息(信息最終要發給App2)
2、Gate1將信息投遞給logic
3、Logic收到信息后,將信息進行存儲
4、存儲成功后,logic向gate1發送ack
5、Gate1將ack信息發給App1
6、Logic檢索redis,查找App2狀態。如果App2未登錄,流程結束
7、如果App2登錄到了gate2,logic將消息發往gate2
8、Gate2將消息發給App2(如果發現App2不在線,丟棄消息即可,這種概率極低,后續離線消息可保證消息不丟)
9、App2向gate2發送ack
10、Gate2將ack信息發給logic
11、Logic將消息狀態設置為已送達。
注:在第6步和第7步之間,啟動計時器(DelayedQueue或哈希環,時間如5秒),計時器時間到后,探測該條消息狀態,如果消息未送達,考慮通過APNS、米推、個推進行推送
1.2.2.7 群聊(c2g)
采用擴散寫(而非擴散讀)的方式。
群聊是多人社交的基本訴求,一個群友在群內發了一條消息:
(1)在線的群友能第一時間收到消息
(2)離線的群友能在登陸后收到消息
由于“消息風暴擴散系數”的存在,群消息的復雜度要遠高于單對單消息。
群基礎表:用來描述一個群的基本信息
im_group_msgs(group_id, group_name,create_user, owner, announcement, create_time)
群成員表:用來描述一個群里有多少成員
im_group_users(group_id, user_id)
用戶接收消息表:用來描述一個用戶的所有收到群消息(與單對單消息表是同一個表)
im_message_recieve(msg_id,msg_from,msg_to, group_id,msg_seq, msg_content, send_time, msg_type, deliverd, cmd_id)
用戶發送消息表:用來描述一個用戶發送了哪些消息
im_message_send (msg_id,msg_from,msg_to, group_id,msg_seq, msg_content, send_time, msg_type, cmd_id)
業務場景舉例:
(1)一個群中有x,A,B,C,D共5個成員,成員x發了一個消息
(2)成員A與B在線,期望實時收到消息
(3)成員C與D離線,期望未來拉取到離線消息
群聊流程如下圖所示
1、X向gate發送信息(信息最終要發給這個群,A、B在線)
2、Gate將消息發給logic
3、存儲消息到im_message_send表,按照msg_from水平分庫
4、回ack
5、回ack
6、Logic檢索數據庫(需要使用緩存),獲得群成員列表
7、存儲每個用戶的消息數據(用戶視圖),按照msg_to水平分庫(并發、批量寫入)。
8、查詢用戶在線狀態及位置
9、Logic向gate投遞消息
10、Gate向用戶投遞消息
11、App返回收到消息的ack信息
12、Gate向logic傳遞ack信息
13、向緩存(Hash)中更新收到ack的時間。然后在通過一個定時任務,每隔一定時間,將數據更新到數據庫(注意只需要寫入時間段內有變化的數據)。
1.2.2.8拉取離線消息
下圖中,將gate和logic合并為im-server。拉取離線消息流程如下。?
1、App端登錄成功后(或業務觸發拉取離線消息),向IM系統發起拉離線消息請求。傳遞3個主要參數,uid表明用戶;msgid表明當前收到的最大消息id(如果沒收到過消息,或拿不到最大消息id則msgid=0)即可;size表示每次拉取條數(這個值也可以由服務器端控制)。
2、假設msgid==0,什么都不做。(參看第6步驟)
3、Im-server查詢用戶前10條離線消息
4、將離線消息推給用戶。假設這10條離線消息最大msgid=110。
5、App得到數據,判斷得到的數據不為空(表明可能沒有拉完離線數據,不用<10條做判斷拉完條件,因為服務端需要下下次拉離線的請求來確定這次數據已送達),繼續發起拉取操作。Msgid=110(取得到的離線消息中最大的msgid)。
6、Im-server刪除該用戶msgid<110的離線消息(或者標記為已送達)。
7、查詢msgid>110的錢10條離線數據。
8、返回給App
……
N-1、查詢msgid>140的離線數據,0條(沒有離線數據了)。
N ?、將數據返回App,App判斷拉取到0條數據,結束離線拉取過程。
1.2.3 PUSH
ISO采用APNS;Android真后臺保活,同時增加米推、個推。
基本思路:push提示信息,App通過拉離線獲得真實消息。
另附文檔說明此問題。
2 協議設計
2.1 TCP數據協議
TCP的數據協議如下圖所示。包括header和body兩部分。
消息頭總共20個字節,具體信息如下表。
2.2TCP消息體設計
消息體協議采用ProtocolBuffer(谷歌)協議,版本3.0.0,該協議在序列化效率、壓縮、可擴展方面都具有優勢。協議條目見附錄11.1.1TCP協議命令清單。以下為主要流程涉及的協議
2.2.1 認證(auth)?
2.2.2?登出(logout)
?
2.2.3 踢人(kickout)?
2.2.4 心跳(keepalive,noop)
心跳包消息體為空。
2.2.5單對單聊天(c2c)
?
2.2.6群聊(c2g)
?
2.2.7拉離線(pull)
2.2.8控制類(ctrl)
?
3 存儲設計
3.1MySQL數據庫
MySQL數據庫采用utf8mb4編碼格式(emoji字符問題)
3.1.1主要表結構
3.1.1.1發送消息表
保存某個用戶發送了哪些消息,用于復現用戶聊天場景(消息漫游功能需要)。
3.1.1.2推送消息表
保存某個用戶收到了哪些消息
3.1.1.3群相關表
群基本信息表
群用戶關系表
3.1.2 水平分庫
3.2Redis緩存
3.2.1用戶狀態及路由信息
Redis緩存以uid為key,檢索channel(socketid),last_packet_time等。
Gate層,session以channel(socketed)為key,檢索uid,及其他信息。
交互接口:gate->logic,通過將channel轉換為uid作為key。
logic->gate,將uid轉換為channel作為key。
3.2.2其他緩存信息
你覺得該怎么存就怎么存。
3.3文件及圖片存儲
采用商用云存儲。
3.4數據歸檔
可考慮采用HBase,HDFS作為數據歸檔,或者相關云存儲服務。
安全部分略,其他非核心功能略
相關閱讀
《IM系統的SESSION結構》
《IM系統如何調試TCP協議》
《NAT是怎么回事》
《視頻聊天功能如何穿透NAT》
《IM移動端怎么搜索本地聊天記錄》
·END·
碼農吹牛逼
互聯網相關技術
微信ID:farmerbrag
總結
以上是生活随笔為你收集整理的一个海量在线用户即时通讯系统(IM)的完整设计的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 网上拍卖网站
- 下一篇: java基于springboot房产备案