Jack Jiang

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

          1、前言


          IM的群聊消息,究竟存1份(即擴(kuò)散讀方式)還是存多份(即擴(kuò)散寫方式)?

          上一篇文章《IM群聊消息的已讀回執(zhí)功能該怎么實(shí)現(xiàn)?》是說,“很容易想到,是存一份”,被網(wǎng)友們罵了,大家爭論的很激烈(見下圖)。

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

          網(wǎng)友罵的對(duì),任何技術(shù)方案,都不是天才般靈感乍現(xiàn)想到的,一定是一個(gè)演進(jìn)迭代,逐步優(yōu)化的過程。今天就聊一聊,IM群聊消息,為啥只需要存一份。

          不過,從公開的技術(shù)資料來看,微信的群聊消息應(yīng)該使用的是存多份(即擴(kuò)散寫方式),詳細(xì)的方案可以在微信團(tuán)隊(duì)分享的這篇文章里找到答案:《微信后臺(tái)團(tuán)隊(duì):微信后臺(tái)異步消息隊(duì)列的優(yōu)化升級(jí)實(shí)踐分享》。

          學(xué)習(xí)交流:

          - 即時(shí)通訊開發(fā)交流3群:185926912[推薦]

          - 移動(dòng)端IM開發(fā)入門文章:《新手入門一篇就夠:從零開發(fā)移動(dòng)端IM

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


          2、本文作者


          IM群聊消息究竟是存1份(即擴(kuò)散讀)還是存多份(即擴(kuò)散寫)?_58同城沈劍.jpg 
          沈劍:58技術(shù)委員會(huì)主席,58高級(jí)架構(gòu)師,58到家技術(shù)總監(jiān)。C2C技術(shù)部負(fù)責(zé)人,58技術(shù)學(xué)院優(yōu)秀講師。

          沈劍的另外幾篇有關(guān)IM的文章也值得你去閱讀:


          3、IM開發(fā)干貨系列文章


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


          另外,如果您是IM開發(fā)初學(xué)者,強(qiáng)烈建議首先閱讀《新手入門一篇就夠:從零開發(fā)移動(dòng)端IM》。

          4、更多關(guān)于IM群聊的文章


          IM系統(tǒng)中的群聊功能,是個(gè)很大話題,下面幾篇在關(guān)群聊的文章您也可以讀一讀:

          >> 更多同類文章 ……

          另外,《一套海量在線用戶的移動(dòng)端IM架構(gòu)設(shè)計(jì)實(shí)踐分享(含詳細(xì)圖文)》一文中也包含了群聊的完整設(shè)計(jì),如果您設(shè)計(jì)IM不知從何下手,可以詳細(xì)地參考此文。

          5、最基本的方案:“在線的群友不存儲(chǔ)消息,離線的群友才存儲(chǔ)”


          群信息,用戶信息,群成員關(guān)系都是基礎(chǔ)數(shù)據(jù):

          group_info(gid, group_info);
          user_info(uid, user_info);
          group_members(gid, uid);


          假設(shè)一個(gè)群(gid)里有4個(gè)成員,其中三個(gè)在線(A, uid1, uid2),一個(gè)不在線(uid3)。

          A發(fā)送了一條消息,很容易想到,對(duì)于不同的群友消息存多份,每個(gè)群友一個(gè)隊(duì)列來存儲(chǔ)。但由于在線的用戶會(huì)實(shí)時(shí)的收到消息,所以暫定只為離線的用戶存儲(chǔ)

          用戶收到的群消息,也是基礎(chǔ)數(shù)據(jù):

          user_msgs(uid,msgid,gid,sender_uid,time,content);


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

          很容易想到,整個(gè)群消息的發(fā)送流程如上圖1-4:

          • 1)發(fā)送消息;
          • 2)查詢狀態(tài);
          • 3)不在線的存儲(chǔ)離線;
          • 4)在線的實(shí)時(shí)推送。

          “在線的群友不存儲(chǔ),離線的群友才存儲(chǔ)”會(huì)帶來的問題是,如果第四步發(fā)生異常,群友會(huì)丟失消息。

          6、優(yōu)化的方案:“不管群員是否在線,都要先存儲(chǔ)消息”


          消息的可達(dá)性是聊天系統(tǒng)中最重要的要素(沒有之一),故這個(gè)方案是不行的,需要優(yōu)化為“不管是否在線,都要先存儲(chǔ)”。

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

          發(fā)送群消息的流程優(yōu)化為,如上圖1-4:

          • 1)發(fā)送消息;
          • 2)所有人都存一份;
          • 3)查詢狀態(tài);
          • 4)在線的實(shí)時(shí)推送。

          先將消息落地,能夠保證消息可達(dá)性,那何時(shí)才能刪除已經(jīng)落地的群消息呢?我們繼續(xù)往下看。

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

          對(duì)于在線的群友:收到群消息后,給個(gè)ack確認(rèn)才能刪除。

          畫外音:邏輯刪除,還是物理刪除,根據(jù)業(yè)務(wù)是否有消息漫游決定。

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

          對(duì)于離線的群友:在下次登陸后,拉取完離線消息再給ack確認(rèn)才能刪除。

          總之:為了保證消息的可達(dá)性,不管是在線消息還是離線消息,必須接收方給ack確認(rèn),才能刪除消息。

          7、“不管群員是否在線,都冗余一份群消息”帶來的問題


          “不管是否在線,都冗余一份群消息”帶來的問題是:同一條消息存儲(chǔ)了很多次,對(duì)磁盤和帶寬造成了很大的浪費(fèi)。

          很容易想到的優(yōu)化是:群消息實(shí)體存儲(chǔ)一份,用戶只冗余消息ID。

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

          故基礎(chǔ)數(shù)據(jù)可以由:

          user_msgs(uid,msgid,gid,sender_uid,time,content);
          優(yōu)化為:
          group_msgs(msgid,gid,sender_uid,time,content);
          user_msgs(uid, msgid, gid);


          這個(gè)優(yōu)化,對(duì)于消息投遞,以及消息刪除的核心流程沒有影響,幾個(gè)實(shí)踐為:

          • 在線用戶投遞消息實(shí)體,ack消息ID;
          • 離線用戶先拉取消息ID,再拉取消息實(shí)體,再ack消息ID。

          如此這般,假如在某個(gè)群友A期間,群里陸續(xù)發(fā)送了N條消息,則user_msgs(uid, msgid, gid)里,會(huì)有 uidA -> mid1,mid2, mid3, … midN 等N條離線記錄,拉取離線消息時(shí),可以把這N條消息一次性拉取出來,然后再刪除:

          delete from user_msgs  where msgid in($mid1,$mid2…, $midN) and gid=$gid


          8、終級(jí)方案:利用群消息的“偏序”特性優(yōu)雅地實(shí)現(xiàn)“只存1份”


          然而,群消息具備“偏序”特性,上面的一次性刪除完全可以優(yōu)化為:

          delete from user_msgs 
          where msgid >= $mid1 and gid=$gid


          這就意味著,每個(gè)用戶只需要記錄“最近一次收到的消息ID”,而不用記錄“所有未收到的消息ID集合”,每當(dāng)收在線消息ack,以及拉離線消息ack時(shí),只需要更新這個(gè)“最近一次收到的消息ID”即可。

          于是乎,基礎(chǔ)數(shù)據(jù)可以由:
          group_members(gid, uid);
          group_msgs(msgid,gid,sender_uid,time,content);
          user_msgs(uid, msgid, gid);

          優(yōu)化為:
          group_members(gid, uid, last_ack_msgid);
          group_msgs(msgid,gid,sender_uid,time,content);
          user_msgs(uid, msgid, gid); // 不再需要

          IM群聊消息究竟是存1份(即擴(kuò)散讀)還是存多份(即擴(kuò)散寫)?_6.jpg 
          即:群消息只存儲(chǔ)一份,群友無需冗余任何消息實(shí)體,或者消息ID了。

          IM群聊消息究竟是存1份(即擴(kuò)散讀)還是存多份(即擴(kuò)散寫)?_7.jpg 
          對(duì)于在線的群友:收到群消息后,修改這個(gè)last_ack_msgid。

          IM群聊消息究竟是存1份(即擴(kuò)散讀)還是存多份(即擴(kuò)散寫)?_8.jpg 
          對(duì)于離線的群友:拉取群消息后,也修改這個(gè)last_ack_msgid。

          畫外音:這里的討論,僅限于接收方收到了哪些消息,和發(fā)送方的已讀回執(zhí)沒有關(guān)系。(這里指的是作者的上篇文章《IM群聊消息的已讀回執(zhí)功能該怎么實(shí)現(xiàn)?》)

          9、本文小結(jié)


          任何架構(gòu)方案都不是靈光一現(xiàn),而是逐步迭代優(yōu)化產(chǎn)生的:

          • 方案1:群聊消息存多份,只存在線,消息容易丟;
          • 方案2:群聊消息存多份,所有群友都存儲(chǔ),消息冗余多;
          • 方案3:群聊消息存多份,只存ID,未利用偏序;
          • 終極方案:群聊消息存一份,只存last_ack_msgid。

          架構(gòu)不(只)是設(shè)計(jì)出來的,更是演進(jìn)出來的。
          (本文同步發(fā)布于:http://www.52im.net/thread-1616-1-1.html

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


          只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


          網(wǎng)站導(dǎo)航:
           
          Jack Jiang的 Mail: jb2011@163.com, 聯(lián)系QQ: 413980957, 微信: hellojackjiang
          主站蜘蛛池模板: 菏泽市| 都匀市| 大城县| 新干县| 务川| 榆社县| 绥滨县| 湾仔区| 中西区| 福清市| 长垣县| 涞源县| 鱼台县| 延安市| 曲麻莱县| 伊川县| 恩施市| 美姑县| 区。| 巴塘县| 绵阳市| 竹山县| 蕉岭县| 德阳市| 绍兴市| 繁昌县| 汾阳市| 海淀区| 开阳县| 巴南区| 双鸭山市| 梅河口市| 邢台市| 班玛县| 云浮市| 廊坊市| 古蔺县| 麻江县| 莎车县| 新乐市| 阜城县|