<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          群消息已讀回執(zhí)(這個屌),究竟是推還是拉?

          共 2966字,需瀏覽 6分鐘

           ·

          2021-01-09 20:01

          每當(dāng)發(fā)出一條微信消息,都希望對方盡快看到,并盡快回復(fù),但始終不知道對方是否閱讀。

          每當(dāng)收到一條不能立馬回復(fù)的微信消息,都默默返回,假裝沒看見。
          畫外音:不想回復(fù)的人,唉,你只是個好人。

          微信用于個人社交,產(chǎn)品設(shè)計上,在線狀態(tài),強(qiáng)制已讀回執(zhí)都有可能暴露個人隱私,故微信并無相關(guān)功能。

          釘釘用于商務(wù)交流,其“強(qiáng)制已讀回執(zhí)”功能,讓職場人無法再“假裝不在線”,“假裝沒收到”。

          有甚者,釘釘?shù)娜河小皬?qiáng)制已讀回執(zhí)”功能,你在群里發(fā)出的消息,能夠知道誰讀了消息,誰沒有讀消息。

          群消息的流程如何,接收方如何確保收到群消息,發(fā)送方如何收已讀回執(zhí),究竟是拉取,還是推送,是今天要聊的話題。

          一、群消息投遞流程,以及可達(dá)性保證
          大家一起跟著樓主的節(jié)奏,一步一步來看群消息怎么設(shè)計。

          核心問題1:群消息,只存一份?還是,每個成員存一份?
          存一份,為每個成員設(shè)置一個群消息隊列,會有大量數(shù)據(jù)冗余,并不合適。

          核心問題2:如果群消息只存一份,怎么知道每個成員讀了哪些消息?
          :可以利用群消息的偏序關(guān)系,記錄每個成員的last_ack_msgid(last_ack_time),這條消息之前的消息已讀,這條消息之后的消息未讀。該方案意味著,對于群內(nèi)的每一個用戶,只需要記錄一個值即可。

          解答上述兩個核心問題后,很容易得到群消息的核心數(shù)據(jù)結(jié)構(gòu)。
          群消息表:記錄群消息。
          group_msgs(msgid, gid, sender_uid, time, content);
          各字段的含義為:消息ID,群ID,發(fā)送方UID,發(fā)送時間,發(fā)送內(nèi)容。

          群成員表:記錄群里的成員,以及每個成員收到的最后一條群消息。
          group_users(gid, uid, last_ack_msgid);
          各字段的含義為:群ID,群成員UID,群成員最后收到的一條群消息ID。

          在核心數(shù)據(jù)結(jié)構(gòu)設(shè)計完之后,一起來看看群消息發(fā)送的流程

          業(yè)務(wù)場景:
          (1)一個群中有A, uid1, uid2, uid3四名成員;
          (2)A, uid1, uid2在線,期望實時收到在線消息;
          (3)uid3離線,期望未來拉取到離線消息;


          其整個消息發(fā)送的流程1-4如上圖:
          (1)A發(fā)出群消息;
          (2)server收到消息后,一來要將群消息落地,二來要查詢?nèi)豪镉心男┤撼蓡T,以便實施推送;
          (3)對于群成員,查詢在線狀態(tài);
          (4)對于在線的群成員,實施推送;

          這個流程里,只要第二步消息落地完成,就能保證群消息不會丟失。

          核心問題3:如何保證接收方一定收到群消息?
          :各個收到消息后,要修改各群成員的last_ack_msgid,以告訴系統(tǒng),這一條消息確認(rèn)收到了。

          在線消息,離線消息的last_ack_msgid的修改,又各有不同。


          對于在線的群友,收到群消息后,第一時間會ack,修改last_ack_msgid


          對于離線的群友,會在下一次登錄時,拉取未讀的所有群離線消息,并last_ack_msgid修改為最新的一條消息。

          核心問題4:如果ack丟失,群友會不會拉取重復(fù)的群消息?
          ,可以根據(jù)msgid在客戶端本地做去重,即使系統(tǒng)層面收到了重復(fù)的消息,仍然可以保證良好的用戶體驗。

          上述流程,只能確保接收方收到消息,發(fā)送方仍然不知道哪些人在線閱讀了消息,哪些人離線未閱讀消息,并沒有實現(xiàn)已讀回執(zhí),那已讀回執(zhí)會對系統(tǒng)設(shè)計產(chǎn)生什么樣的影響呢?

          二、已讀回執(zhí)流程
          對于發(fā)送方發(fā)送的任何一條群消息,都需要知道,這條消息有多少人已讀多少人未讀,就需要一個基礎(chǔ)表來記錄這個關(guān)系。

          消息回執(zhí)表:用來記錄消息的已讀回執(zhí)。
          msg_acks(sender_uid, msgid, recv_uid, gid,if_ack);
          各字段的含義為:發(fā)送方UID,消息ID,回執(zhí)方UID,群ID,回執(zhí)標(biāo)記。

          增加了已讀回執(zhí)邏輯后,群消息的流程會有細(xì)微的改變。


          步驟二,server收到消息后,除了要:
          (1)將群消息落地;
          (2)查詢?nèi)豪镉心男┤撼蓡T,以便實施推送;
          之外,還需要:
          (3)插入每條消息的初始回執(zhí)狀態(tài);


          接收方修改last_ack_msgid的流程,會變?yōu)椋?br>
          (1)發(fā)送ack請求;
          (2)修改last_ack_msgid,并且,修改已讀回執(zhí)if_ack狀態(tài);
          (3)查詢發(fā)送方在線狀態(tài);
          (4)向發(fā)送方實時推送已讀回執(zhí)(如果發(fā)送方在線);

          如果發(fā)送方不在線,ta會在下次登錄的時候:
          (5)從關(guān)聯(lián)表里拉取每條消息的已讀回執(zhí);

          這里的初步結(jié)論是:
          (1)如果發(fā)送方在線,會實時被推送已讀回執(zhí);
          (2)如果發(fā)送方不在線,會在下次在線時拉取已讀回執(zhí);

          三、流程優(yōu)化方案
          再次詳細(xì)的分析下,群消息已讀回執(zhí)的“消息風(fēng)暴擴(kuò)散系數(shù)”,假設(shè)每個群有200個用戶,其中20%的用戶在線,即40各用戶在線。群用戶每發(fā)送一條群消息,會有:
          (1)40個消息,通知給群友;
          (2)40個ack修改last_ack_msgid,發(fā)給服務(wù)端;
          (3)40個已讀回執(zhí),通知給發(fā)送方;
          可見,其消息風(fēng)暴擴(kuò)散系數(shù)非常之大

          同時:
          (1)需要存儲40條ack記錄;
          群數(shù)量,群友數(shù)量,群消息數(shù)量越來越多之后,存儲也會成為問題。

          是否有優(yōu)化方案呢?

          群消息的推送,能否改為接收方輪詢拉???
          不能,消息接收,實時性是核心指標(biāo)。

          對于last_ack_msgid的修改,真的需要每個群消息都進(jìn)行ack么?
          其實不需要,可以批量ack,累計收到N條群消息(例如10條),再向服務(wù)器發(fā)送一次last_ack_msgid的修改請求,同時修改這個請求之前所有請求的已讀回執(zhí),這樣就能將40個發(fā)送給服務(wù)端的ack請求量,降為原來的1/10。

          會帶來什么副作用?
          last_ack_msgid的作用是,記錄接收方最近新取的一條群消息,如果不實時更新,可能導(dǎo)致,異常退出時,有一些群消息沒來得及更新last_ack_msgid,使得下次登陸時,拉取到重復(fù)的群消息。但這不是問題,客戶端可以根據(jù)msgid去重,用戶體驗不會受影響。

          發(fā)送方在線時,對于已讀回執(zhí)的發(fā)送,真的需要實時推送么?
          其實不需要,發(fā)送方每發(fā)一條消息,會收到40個已讀回執(zhí),采用輪詢拉取(例如1分鐘一次,一個小時也就60個請求),可以大大降低請求量。
          畫外音:或者直接放到應(yīng)用層keepalive請求里,做到0額外請求增加。

          會帶來什么副作用?
          已讀回執(zhí)更新不實時,最壞的情況下,1分鐘才更新回執(zhí)。當(dāng)然,可以根據(jù)性能與產(chǎn)品體驗來折衷配置這個輪詢時間。

          如何降低數(shù)據(jù)量?
          答:回執(zhí)數(shù)據(jù)不是核心數(shù)據(jù)
          (1)已讀的消息,可以進(jìn)行物理刪除,而不是標(biāo)記刪除;
          (2)超過N長時間的回執(zhí),歸檔或者刪除掉;

          四、總結(jié)
          對于群消息已讀回執(zhí),一般來說:
          (1)如果發(fā)送方在線,會實時被送已讀回執(zhí);
          (2)如果發(fā)送方不在線,會在下次在線時取已讀回執(zhí);

          如果要對進(jìn)行優(yōu)化,可以:
          (1)接收方累計收到N條群消息再批量ack;
          (2)發(fā)送方輪詢拉取已讀回執(zhí);
          (3)物理刪除已讀回執(zhí)數(shù)據(jù),定時刪除或歸檔非核心歷史數(shù)據(jù);

          推還是拉?任何脫離業(yè)務(wù)的架構(gòu)設(shè)計都是耍流氓。

          架構(gòu)師之路-分享技術(shù)思路


          若有收獲,隨手轉(zhuǎn)發(fā)。
          瀏覽 39
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  小早川怜子爆乿护士中文在线 | 亚洲狼人综合 | 97啪啪视频 | 无码精品一区二区三区四区网站 | 日韩黄色视频在线 |