單聊
- 發送方發送一條消息
- 服務端消息入庫,檢查接收方在線狀態。若在線,推送;不在線,接收方登錄時拉取
- 接收方收到消息,發送已讀回執
- 服務端收到回執并入庫,通知發送方
- 發送方更新本地數據庫,修改已讀狀態
群聊
群消息投遞流程,以及可達性保證
核心問題
- 群消息,只存一份?還是,每個成員存一份?
存一份 - 如果群消息只存一份,怎么知道每個成員讀了哪些消息?
記錄每個成員的last_ack_msgid - 如何保證接收方一定收到群消息?
各個群成員收到消息后,要修改各群成員的last_ack_msgid,以告訴系統,這一條消息確認收到了 - 如果ack丟失,群友會不會拉取重復的群消息?
會,可以根據msgid在客戶端本地做去重
核心數據結構
- 群消息表:記錄群消息。
group_msgs(msgid, gid, sender_uid, time, content);
各字段的含義為:消息ID,群ID,發送方UID,發送時間,發送內容。 - 群成員表:記錄群里的成員,以及每個成員收到的最后一條群消息。
group_users(gid, uid, last_ack_msgid);
各字段的含義為:群ID,群成員UID,群成員最后收到的一條群消息ID。
群消息發送的流程
群消息發送的流程
- A發出群消息
- server收到消息后,一來要將群消息落地,二來要查詢群里有哪些群成員,以便實施推送
- 對于群成員,查詢在線狀態
- 對于在線的群成員,實施推送
群消息確認流程
在線成員
離線成員
已讀回執流程
對于發送方發送的任何一條群消息,都需要知道,這條消息有多少人已讀多少人未讀,就需要一個基礎表來記錄這個關系。
消息回執表:用來記錄消息的已讀回執。
msg_acks(sender_uid, msgid, recv_uid, gid, if_ack);
各字段的含義為:發送方UID,消息ID,回執方UID,群ID,回執標記。
群消息流程
消息流程
- 將群消息入庫
- 查詢群里有哪些群成員,以便實施推送
-
插入每條消息的初始回執狀態
發送方已讀回執 - 發送ack請求
- 修改last_ack_msgid,并且,修改已讀回執if_ack狀態
- 查詢發送方在線狀態
- 向發送方實時推送已讀回執(如果發送方在線)
- 如果發送方不在線,ta會在下次登錄的時候,從關聯表里拉取每條消息的已讀回執
流程優化方案
群消息已讀回執的“消息風暴擴散系數”
假設每個群有200個用戶,其中20%的用戶在線,即40各用戶在線。群用戶每發送一條群消息,會有:
- 40個消息,通知給群友
- 40個ack修改last_ack_msgid,發給服務端
- 40個已讀回執,通知給發送方
- 需要存儲40條ack記錄
優化方案
- 群消息的推送,能否改為接收方輪詢拉取?
答:不能,消息接收,實時性是核心指標。 - 對于last_ack_msgid的修改,真的需要每個群消息都進行ack么?
答:其實不需要,可以批量ack。有副作用(不實時,拉取重復消息) - 發送方在線時,對于已讀回執的發送,真的需要實時推送么?
答:其實不需要,發送方每發一條消息,會收到40個已讀回執,采用輪詢拉取或放入keepalive請求里。副作用是不實時。