IM羣聊消息的已讀回執功能該怎麼實現?

本文引用了架構師之路公衆號做者沈劍的文章,內容有改動,感謝原做者。html

一、前言


咱們平時在使用即時通信應用時候,每當發出一條聊天消息,都但願對方儘快看到,並儘快回覆,但對方到底有沒有真的看到?我卻並不知道。

一個殘酷的現實是,不少時候對方實際上是早就已經看到了這條消息,但出出種種緣由(你們都懂的),一般都是默默返回——僞裝沒看見。

像微信這樣的熟人社交工具,在產品的設計理念上,爲了保持使用者的隱私性,在線狀態、已讀回執等涉及隱私的功能,都沒有提供。但不少時候,尤爲商務、辦公場合下,特別須要一種強反饋的工具,這對於打造高效的團隊頗有幫助(雖然員工很反感,但老闆都喜歡這樣的功能,哈哈)。

目前市面上主流的移動端IM裏,提供了已讀回執的主要有阿里的釘釘、網易的易信、阿里的旺旺,以下圖所示:
<ignore_js_op>IM羣聊消息的已讀回執功能該怎麼實現?_22.jpg    <ignore_js_op>IM羣聊消息的已讀回執功能該怎麼實現?_11.jpg    <ignore_js_op>IM羣聊消息的已讀回執功能該怎麼實現?_33.jpg 
▲ 上圖從左至右分別爲:釘釘、易信、旺旺(千牛)

以阿里的釘釘爲例,釘釘的產品定位是用於商務交流,其「強制已讀回執」功能,讓職場人沒法再「僞裝不在線」、「僞裝沒收到」。更有甚者,釘釘的羣聊「強制已讀回執」功能,甚至可以知道誰讀了消息,誰沒有讀消息(老闆的福音啊)

那麼羣聊消息的收發流程、消息的送達保證、已讀回執機制,到底該怎麼實現呢?這就是今天要討論的話題。算法

 

學習交流:數據庫

 

- 即時通信開發交流3羣:185926912[推薦]服務器

- 移動端IM開發入門文章:《新手入門一篇就夠:從零開發移動端IM微信

 

(本文同步發佈於:http://www.52im.net/thread-1611-1-1.html數據結構

二、IM開發乾貨系列文章


本文是系列文章中的第14篇,總目錄以下:

架構


另外,若是您是IM開發初學者,強烈建議首先閱讀《新手入門一篇就夠:從零開發移動端IM》。負載均衡

三、正文引言


首先咱們須要瞭解一下羣消息的設計、投遞流程以及可達性保證機制,因不是本文要討論的重點,因此儘可能言簡意賅,更詳細的資料請見下方的推薦文章列表。

如您對聊天消息的投遞和送達機制等尚無概念,可先讀本系列文章的如下幾篇,有助於您詳細掌握這方面的內容:

工具

 

四、羣消息怎麼設計?


你們一塊兒跟着樓主的節奏,一步一步來看羣消息怎麼設計。

核心問題1:羣消息,只存一份?仍是,每一個成員存一份?
答:存一份,爲每一個成員設置一個羣消息隊列,會有大量數據冗餘,並不合適。

核心問題2:若是羣消息只存一份,怎麼知道每一個成員讀了哪些消息?
答:能夠利用羣消息的偏序關係,記錄每一個成員的last_ack_msgid(last_ack_time),這條消息以前的消息已讀,這條消息以後的消息未讀。該方案意味着,對於羣內的每個用戶,只須要記錄一個值便可。

解答上述兩個核心問題後,很容易獲得羣消息的核心數據結構。

羣消息表:記錄羣消息性能

group_msgs(msgid, gid, sender_uid, time, content);


各字段的含義爲:消息ID,羣ID,發送方UID,發送時間,發送內容。

羣成員表:記錄羣裏的成員,以及每一個成員收到的最後一條羣消息

group_users(gid, uid, last_ack_msgid);


各字段的含義爲:羣ID,羣成員UID,羣成員最後收到的一條羣消息ID。

五、瞭解一下羣消息發送的流程


在覈心數據結構設計完以後,一塊兒來看看羣消息發送的流程(本系列中的文章《IM羣聊消息如此複雜,如何保證不丟不重?》詳細講解了這個過程,能夠深刻讀一讀)。

業務場景:

  • 1)一個羣中有A, uid1, uid2, uid3四名成員;
  • 2)A, uid1, uid2在線,指望實時收到在線消息;
  • 3)uid3離線,指望將來拉取到離線消息。


<ignore_js_op>IM羣聊消息的已讀回執功能該怎麼實現?_1.jpg 

其整個消息發送的流程1-4如上圖:

  • 1)A發出羣消息;
  • 2)server收到消息後,一來要將羣消息落地,二來要查詢羣裏有哪些羣成員,以便實施推送;
  • 3)對於羣成員,查詢在線狀態;
  • 4)對於在線的羣成員,實施推送。


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

核心問題3:如何保證接收方必定收到羣消息?
答:各個收到消息後,要修改各羣成員的last_ack_msgid,以告訴系統,這一條消息確認收到了。

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

<ignore_js_op>IM羣聊消息的已讀回執功能該怎麼實現?_2.jpg 
對於在線的羣友,收到羣消息後,第一時間會ack、修改last_ack_msgid

<ignore_js_op>IM羣聊消息的已讀回執功能該怎麼實現?_3.jpg 
對於離線的羣友,會在下一次登陸時,拉取未讀的全部羣離線消息,並將last_ack_msgid修改成最新的一條消息

核心問題4:若是ack丟失,羣友會不會拉取重複的羣消息?
答:,能夠根據msgid在客戶端本地作去重,即便系統層面收到了重複的消息,仍然能夠保證良好的用戶體驗。

上述流程,只能確保接收方收到消息,發送方仍然不知道哪些人在線閱讀了消息,哪些人離線未閱讀消息,並無實現已讀回執,那已讀回執會對系統設計產生什麼樣的影響呢?

六、已讀回執流程的設計


前面的基礎知識咱們已經瞭解的差很少,本節來討論本文的重點內容,即羣聊已讀回執流程到底該怎麼設計。

對於發送方發送的任何一條羣消息,都須要知道,這條消息有多少人已讀多少人未讀,就須要一個基礎表來記錄這個關係

消息回執表:用來記錄消息的已讀回執

msg_acks(sender_uid, msgid, recv_uid, gid,if_ack);


各字段的含義爲:發送方UID,消息ID,回執方UID,羣ID,回執標記。

增長了已讀回執邏輯後,羣消息的流程會有細微的改變,見下圖:
<ignore_js_op>IM羣聊消息的已讀回執功能該怎麼實現?_4.jpg 

接着,server收到消息後,除了要:

  • 1)將羣消息落地;
  • 2)查詢羣裏有哪些羣成員,以便實施推送;


以外,還須要:

  • 3)插入每條消息的初始回執狀態。


<ignore_js_op>IM羣聊消息的已讀回執功能該怎麼實現?_5.jpg 

接收方修改last_ack_msgid的流程,會變爲:

  • 1)發送ack請求;
  • 2)修改last_ack_msgid,而且,修改已讀回執if_ack狀態;
  • 3)查詢發送方在線狀態;
  • 4)向發送方實時推送已讀回執(若是發送方在線);


若是發送方不在線,ta會在下次登陸的時候:

  • 5)從關聯表裏拉取每條消息的已讀回執。


這裏的初步結論是:

  • 若是發送方在線:會實時被推送已讀回執;
  • 若是發送方不在線:會在下次在線時拉取已讀回執。

 

七、已讀回執流程優化方案


再次詳細的分析下,羣消息已讀回執的「消息風暴擴散係數」,假設每一個羣有200個用戶,其中20%的用戶在線,即40各用戶在線。

那麼,羣用戶每發送一條羣消息,會有:

  • 40個消息,通知給羣友;
  • 40個ack修改last_ack_msgid,發給服務端;
  • 40個已讀回執,通知給發送方。


可見,其消息風暴擴散係數很是之大

同時:

  • 須要存儲40條ack記錄。


羣數量,羣友數量,羣消息數量愈來愈多以後,存儲也會成爲問題

是否有優化方案呢?

羣消息的推送,可否改成接收方輪詢拉取?
答:不能,消息接收,實時性是核心指標。

對於last_ack_msgid的修改,真的須要每一個羣消息都進行ack麼?
答:其實不須要,能夠批量ack,累計收到N條羣消息(例如10條),再向服務器發送一次last_ack_msgid的修改請求,同時修改這個請求以前全部請求的已讀回執,這樣就能將40個發送給服務端的ack請求量,降爲原來的1/10。

會帶來什麼反作用?
答:last_ack_msgid的做用是,記錄接收方最近新取的一條羣消息,若是不實時更新,可能致使,異常退出時,有一些羣消息沒來得及更新last_ack_msgid,使得下次登錄時,會拉取到重複的羣消息。但這不是問題,客戶端能夠根據msgid去重,用戶體驗不會受影響

發送方在線時,對於已讀回執的發送,真的須要實時推送麼?
答:其實不須要,發送方每發一條消息,會收到40個已讀回執,採用輪詢拉取(例如1分鐘一次,一個小時也就60個請求),能夠大大下降請求量。
畫外音:或者直接放到應用層keepalive請求裏,作到0額外請求增長。

會帶來什麼反作用?
答:已讀回執更新不實時,最壞的狀況下,1分鐘才更新回執。固然,能夠根據性能與產品體驗來折衷配置這個輪詢時間。

如何下降數據量?
答:回執數據不是核心數據

  • 已讀的消息,能夠進行物理刪除,而不是標記刪除;
  • 超過N長時間的回執,歸檔或者刪除掉。

 

八、本文小結


對於羣消息已讀回執,通常來講:

  • 若是發送方在線,會實時被推送已讀回執;
  • 若是發送方不在線,會在下次在線時拉取已讀回執。


若是要對進行優化,能夠:

  • 接收方累計收到N條羣消息再批量ack;
  • 發送方輪詢拉取已讀回執。


物理刪除已讀回執數據,定時刪除或歸檔非核心歷史數據。

(本文同步發佈於:http://www.52im.net/thread-1611-1-1.html

相關文章
相關標籤/搜索