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