Jack Jiang

          我的最新工程MobileIMSDK:http://git.oschina.net/jackjiang/MobileIMSDK
          posts - 503, comments - 13, trackbacks - 0, articles - 1

          1、引言

          IM系統中,特別是在企業應用場景下,消息的已讀未讀狀態是一個強需求。

          以阿里的釘釘為例,釘釘的產品定位是用于商務交流,其“強制已讀回執”功能,讓職場人無法再“假裝不在線”、“假裝沒收到”。更有甚者,釘釘的群聊“強制已讀回執”功能,甚至能夠知道誰讀了消息,誰沒有讀消息(老板的福音啊)。

           

          ▲ 釘釘里的群聊消息已讀未讀功能效果

          功能看起來很酷,但用起來是一言難盡(上班族心里苦.... )。實際上,技術實現也并不容易。

          那么,對于已讀未讀狀態:

          • 1)如果是私聊:消息的閱讀狀態比較容易實現,在性能和存儲上也不存在問題;
          • 2)如果是群聊:考慮到存儲和處理性能,特別當處于一個云環境時,如何高效地處理群聊的已讀未讀狀態是一個非常值得探討的話題。

          這里提到的“高效”含3個方面:

          • 1)存儲空間;
          • 2)處理速度;
          • 3)傳輸字節數。

          本文將從服務端的角度來探討已讀未讀狀態,在具體的技術實現上對于存儲空間占用方面的思路差異。能力有限,權當個人筆記,歡迎交流。

          學習交流:

          - 即時通訊/推送技術開發交流5群:215477170[推薦]

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

          本文已同步發布于“即時通訊技術圈”公眾號,歡迎關注:

          ▲ 本文在公眾號上的鏈接是:https://mp.weixin.qq.com/s/yUkKPOBsdqLlxiFrGmwFRQ,原文鏈接是:http://www.52im.net/thread-3054-1-1.html

          2、內容點評

          在收錄本文前,Jack Jiang建議原作者對某些具體的技術點進行更深入的分享,但因作者工作較忙,本文中的某些關鍵技術點未來的及作進一步展開。

          所以,本文可以作為IM聊天消息(主要是群聊)中已讀未讀功能的基本實現思路方面的參考,但不建議盲目迷信文中的結論或方案,避免被一些不夠具體的技術指標而誤導。

          3、相關文章

          如果你還想了解更多有關IM群聊中已讀未讀功能的實現邏輯,可以進一步閱讀干貨文章《IM群聊消息的已讀回執功能該怎么實現?》(強烈推薦)。

          如果你對IM中的已讀未讀功能有產品方面的痛點困惑,可以參考一下微信對已讀未讀功能的設計定位,詳見《IM熱門功能思考:為什么微信里沒有消息“已讀”功能?》。

          更多IM群聊技術方面的文章詳見文本附錄部分。

          4、已讀未讀狀態交互流程

          發送者發送的IM聊天消息,在接收者閱讀消息后,是否要求閱讀者通知已讀,可能是由系統配置、組織配置、群組配置等決定,也可能由發送者根據業務需求決定。以下的討論,均假設消息需要已讀未讀狀態。

          客戶端與服務端之間,關于閱讀狀態的命令只需3個,每個命令含請求和應答。

          4.1 通知消息已讀(私聊、群聊通用)

          當小寶閱讀了一條或若干條消息,需向服務端發送消息已讀通知:“眾愛卿發的x+y+z消息,朕已閱”。

          服務端收到小寶的已讀通知時,需完成以下事項:

          • 1)存儲消息的已讀狀態;
          • 2)返回應答給小寶;
          • 3)向已讀列表的消息的原始發送者通知消息已讀。

          對于第“3)”步:

          • 1)私聊的場合,比較好理解,就是發送給私聊的對方;
          • 2)群聊的場合,可很不一樣:因為小寶發送的已讀消息列表,可能是由眾愛卿發送的??紤]這種假設:張三、李四、王五發出的群聊消息,被小寶一下都閱讀了,那么小寶發出的已讀通知包含的消息列表,需要被IMS分解成3個已讀通知(3個不同的消息列表),分別通知給張三、李四、王五,通知內容是“愛卿(不含'"眾")發的這些消息,朕已閱”。

          下面是大致的邏輯流程圖: 

          4.2 查詢消息的未讀人數(私聊、群聊通用)

          消息的發送者,加載消息列表到聊天窗口時,可能需要展示消息是否被已讀。

          對群聊而言,顯示的信息可能是n人未讀的提示,那么需要向服務端查詢消息的未讀人數,由于客戶端可能在UI顯示自己發出的多條消息,需支持一次請求查詢多條消息。

          以未讀人數的方式來表示消息的閱讀狀態,統一了私聊、群聊的查詢,使得客戶端-服務端間的接口更簡單,同時使客戶端的實現邏輯更統一。

          就像下面這樣:

          • 1)對于私聊:如果未讀人數n>0,表示消息未讀;
          • 2)對于群聊:直接顯示n人未讀即可,當然,當n等于0時表示全部已讀。

          4.3 查詢群消息的已讀、未讀人員清單(群聊)

          當客戶端希望顯示某一條群聊消息的已讀、未讀人員列表,需向服務端發起查詢。

          大致的邏輯流程圖如下:

          5、幾種具體的已讀未讀狀態存儲思路探討

          5.1 基本約定

          群聊的閱讀狀態比私聊復雜,因此這里著重討論群聊的閱讀狀態。

          假設群成員數是n,各個客戶端立即IM服務端發送已讀通知。服務端需存儲每個人的閱讀狀態,包括那些未讀的成員。由于群的成員清單可能變化,比如今天增加了一個成員,則昨天發的消息、與今天發的消息,其接收者列表不一樣。

          即:

          • 1)同一個群的不同消息,對應的接收者列表可能不一樣。
          • 2)換言之,每一條消息都需要記錄完整的接收者列表和已讀人員列表。

          為了方便討論,本章假設群成員有640人為前提。

          5.2 存儲思路1

          每一條消息都維護:

          • 1)接收人員列表receiver_list;
          • 2)已讀人員列表read_list。

          具體是:

          • 1)IM Server收到一條消息時,用全體群成員構建receiver_list;
          • 2)IM Server收到群成員對這條消息的已讀通知時,將此成員加入到read_list。

          客戶端獲取此消息的數據:

          • 1)當需要獲取未讀人數時,用receiver_list的個數減去read_list的個數;
          • 2)當需要獲取已讀、未讀人員列表時,需用receiver_list減去read_list得到未讀人員列表。

          那么,思路1每條消息的存儲空間是:

          640個ID + 不定數量的已讀人員ID

          5.3 存儲思路2

          每一條消息維護:

          • 1)未讀人員列表unread_list;
          • 2)已讀人員列表read_list。

          具體是:

          • 1)IM Server收到一條消息時,用全體群成員構建unread_list;
          • 2)IM Server收到群成員對這條消息的已讀通知時,將此成員從unread_list移出,同時加入到read_list。

          客戶端獲取此消息的數據:

          • 1)當需要獲取未讀人數時,直接計算unread_list的個數;
          • 2)當需要獲取已讀、未讀人員列表時,直接返回unread_list和read_list。

          那么,思路2每條消息的存儲空間是:

          未讀人員ID + 已讀人員ID,合計640個ID

          思路2的實現,占用的空間是案1的0.5倍~1.0倍。即案2占用的空間少,但在每次收到客戶端的已讀通知時,比案1多了一個操作:從unread_list進行減員。

          5.4 存儲思路3(我的實現)

          5.4.1)探討5.2節、5.3節的不足:

          5.2節、5.3節這兩種思路,都能滿足功能需求,但存在巨大的存儲浪費。

          該群有640人,如果群內聊天每天有1024條消息,人員ID以4字節存儲計算,那么為該群每天的消息閱讀狀態需要消耗的空間是:

          5.2節思路1:1024 * (640 * 4 + 已讀人數 * 4),范圍是 2.5MB ~ 5MB;

          5.3節思路2:1024 * 640 * 4,等于2.5MB。

          這僅僅是一個群在一天之內產生的閱讀狀態數據,如果是在云平臺運行,單此功能消耗的空間,呵呵~~

          題外話:如果成員不是用4字節整型存儲,而改用字符串,比如"1123356777",那就更可觀了。

          5.4.2)如何減少存儲空間:

          考慮群成員并非時時刻刻都在變化,多數情況下,群成員的列表是相對穩定的,今天的和上周(甚至更久以前)的列表甚至可能是一樣的,那么有可能幾百條消息,甚至幾萬條消息對應的群成員列表是相同的。

          因此,引出本文的重點思想:

          考慮讓不同的消息共用群成員列表,即把消息的閱讀狀態與群成員列表分開存儲,并記錄它們之間的關聯。

          假定平均每1024條消息共用一個群成員列表,發了1024條消息后,群成員變化了,此后需要用新的群成員列表。

          那么這一千條消息的閱讀狀態所占用的空間是:

          群成員列表空間 + 1024條消息的閱讀狀態:640 * 4 + 1024 * 每條消息的閱讀狀態所占空間

          在具備群成員列表的前提下,如何減少每條消息的閱讀狀態所占空間?

          很自然會想到用bit來表示已讀人員,因為一個32位整型可表示32個人的已讀狀態。bit的順序只需與群成員列表的順序一致即可。

          當一條消息沒有人已讀時,閱讀狀態占用0字節;當群內每個人都閱讀時,占用的空間最大,即640 / 32 = 20字節。

          因此優化之后,這一千條消息的閱讀狀態所占用的空間,范圍是2.5KB ~ (2.5KB + 1024 * 20B),即2.5KB ~ 22.5KB,此數值與5.2節思路1、5.3節思路2對比,有了極大幅度地下降。

          如下圖所示:

          該表格的前提條件:

          • 1)一個群有640人;
          • 2)該群連續1024條消息對應的群成員列表是穩定的。

          退一步考慮,哪怕這1024條消息對應的群成員列表不穩定,中間變化了10次,那么也僅會多出2.5KB * 10即25KB的存儲空間,與案1、案2相比仍然有極大優勢。

          6、如何提高已讀未讀狀態的處理速度

          小寶往公司群發了一條消息我來給大家介紹一下新來的女同事,大家立即、馬上、瞬間、閃電般地查看消息,感覺遲1秒就會失去秒殺女神的機會一樣,意味著一瞬間會有N多條已讀通知發送到IMS。

          對這些消息的處理流程是一樣的:

          • 1)可合并這些操作以批量形式進行存儲、轉發;
          • 2)由于存儲消息的閱讀狀態是一個設置bit的過程,所以不存在互斥的問題,即使在分布式環境也可以放心操作;
          • 3)消息對應的成員列表信息可臨時緩存在內存對象內,以減少查詢IO,提高效率。

          附錄:更多IM群聊技術文章

          快速裂變:見證微信強大后臺架構從0到1的演進歷程(一)

          如何保證IM實時消息的“時序性”與“一致性”?

          IM單聊和群聊中的在線狀態同步應該用“推”還是“拉”?

          IM群聊消息如此復雜,如何保證不丟不重?

          微信后臺團隊:微信后臺異步消息隊列的優化升級實踐分享

          移動端IM中大規模群消息的推送如何保證效率、實時性?

          現代IM系統中聊天消息的同步和存儲方案探討

          關于IM即時通訊群聊消息的亂序問題討論

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

          IM群聊消息究竟是存1份(即擴散讀)還是存多份(即擴散寫)?

          一套高可用、易伸縮、高并發的IM群聊、單聊架構方案設計實踐

          [技術腦洞] 如果把14億中國人拉到一個微信群里技術上能實現嗎?

          IM群聊機制,除了循環去發消息還有什么方式?如何優化?

          網易云信技術分享:IM中的萬人群聊技術方案實踐總結

          阿里釘釘技術分享:企業級IM王者——釘釘在后端架構上的過人之處

          IM群聊消息的已讀未讀功能在存儲空間方面的實現思路探討

          >> 更多同類文章 ……

          (本文同步發布于:http://www.52im.net/thread-3054-1-1.html



          作者:Jack Jiang (點擊作者姓名進入Github)
          出處:http://www.52im.net/space-uid-1.html
          交流:歡迎加入即時通訊開發交流群 215891622
          討論:http://www.52im.net/
          Jack Jiang同時是【原創Java Swing外觀工程BeautyEye】【輕量級移動端即時通訊框架MobileIMSDK】的作者,可前往下載交流。
          本博文 歡迎轉載,轉載請注明出處(也可前往 我的52im.net 找到我)。


          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          Jack Jiang的 Mail: jb2011@163.com, 聯系QQ: 413980957, 微信: hellojackjiang
          主站蜘蛛池模板: 宁化县| 贵溪市| 凤冈县| 临潭县| 香港| 阜南县| 青铜峡市| 永新县| 高雄市| 天祝| 台山市| 东兰县| 英吉沙县| 冕宁县| 年辖:市辖区| 凌海市| 广德县| 弋阳县| 枣庄市| 盖州市| 灵寿县| 绥阳县| 方山县| 巴里| 二连浩特市| 壶关县| 治多县| 莱西市| 长白| 桂阳县| 九龙城区| 广宁县| 基隆市| 伊春市| 商水县| 德江县| 庄浪县| 湘乡市| 汨罗市| 儋州市| 华阴市|