Jack Jiang

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

          本文由阿里閑魚技術團隊今朝、有攸分享,本次有修訂。

          1、引言

          閑魚即時消息系統歷經數代迭代,目前已能穩定的支撐億級消息體量。

          在此消息系統的建設過程中,我們經歷了從簡單到復雜、從困擾到破局,每一次的技術改變都是為了更好的解決當下業務所面臨的問題。

          本文分享的是閑魚即時消息系統架構從零開始的技術變遷之路,以期更多的同行們在此基礎上汲取經驗,得到有價值的啟發。

          學習交流:

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

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

          - 開源IM框架源碼:https://github.com/JackJiang2011/MobileIMSDK

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

          2、系列文章

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

          1. 阿里IM技術分享(一):企業級IM王者——釘釘在后端架構上的過人之處
          2. 阿里IM技術分享(二):閑魚IM基于Flutter的移動端跨端改造實踐
          3. 阿里IM技術分享(三):閑魚億級IM消息系統的架構演進之路》(* 本文
          4. 阿里IM技術分享(四):閑魚億級IM消息系統的可靠性投遞技術實踐》(* 稍后發布

          3、1.0版:業務初創期、最小化可用

          3.1 技術背景

          2014年啟動閑置交易獨立APP “閑魚”,一期構建完成APP主鏈路,包含商品:發布→搜索→商品詳情→IM會話→交易

          作為初創app,業務需盡快上線驗證效果,技術建設上需要完成閑魚消息從無到有的系統搭建。

          3.2 技術方案

          作為即時通訊系統,最小化能力包含:

          • 1)消息存儲:會話、摘要、消息;
          • 2)消息同步:推、拉;
          • 3)消息通道:長連接、廠商推送。

          與一般IM會話模型不同的是,閑魚會話以商品為主體,“人+人+商品”為要素構建會話。

          因會話模型的差異,淘系已有的消息系統,短期內無法滿足業務需求,而閑魚完全自建消息系統耗時巨大。

          為了保障業務高效上線,技術選型上最大化復用已有系統能力,避免重復造輪子。

          所以,我們的技術方案是:

          • 1)數據模型與底層存儲依賴淘系私信體系進行建設;
          • 2)消息數據獲取上,客戶端全量從服務端拉取消息數據;
          • 3)通訊協議使用來往SDK及mtop。

          總體架構如下圖,以此模式完成快速交付保障了業務最小化可用:

          4、2.0版:用戶量增速快、需要重建消息系統

          4.1 技術背景

          閑魚用戶量正快速突破100萬,即時消息服務的調用量暴漲。在這樣的背景下,用戶反饋消息數據獲取的卡頓、白屏成為常態,大量的消息Push發送下,系統告警頻發。

          造成這些問題的原因:1.0版的架構模式下,獲取消息數據全量拉模式,客戶端純UI不做數據存儲。

          具體就是:

          • 1)當用戶需要查看消息數據時,數據拉取成功與否取決于網絡、數據訪問速度,偶發性的造成卡頓、白屏;
          • 2)中心化的數據存儲,讀遠大于寫,高并發下,服務端負載過大。

          針對第2)點:比如1W個用戶同時在線聊天,按照當前架構并發拉取全量消息,估算5萬QPS。不妨假設,同時在線聊天用戶數10萬時,對服務端壓力可想而知。

          4.2 技術方案

          基于上述問題,我們決定重建消息系統架構,以應對未來更大的用戶增量。

          回歸到IM系統的核心功能:

          4.2.1)消息存儲模型:

          • 1)會話模型:由owner、itemid、user、sessionType 來標識唯一會話,增加擴展屬性支持個性化;
          • 2)摘要模型:作為用戶會話視圖,同一會話的不同用戶可個性化呈現,由userid、sid標識唯一摘要;
          • 3)消息模型:由sender、消息內容、消息版本、sid組成。sid+消息版本唯一確定一條消息;
          • 4)指令模型:是一種雙端約定,由服務端下發,客戶端執行的指令集。如免打擾指令、刪除指令等。

          4.2.2)消息通道:

          1)在線通道:使用淘寶無線ACCS長連接提供的全雙工、低延時、高安全的通道服務;

          2)離線通道:使用淘寶消息推送平臺AGOO. 其屏蔽了各主流廠商對接的復雜度,直接對業務系統提供服務。

          4.2.3)消息同步模型:

          1)客戶端建立數據庫,存儲消息數據:當消息數據存儲在本地設備上,消息同步從全量拉取優化為全量+增量同步結合的模式。

          增量和全量同步具體指的是:

          • a. 增量同步:客戶端存儲消息位點信息,通過與服務端最新位點比較,僅同步增量消息;
          • b. 全量同步:當用戶卸載重裝或位點gap過大時,客戶端全量拉取歷史消息數據,進行端上數據重建。

          2)服務端建設個人消息域環(收件箱模型):以和客戶端進行增量數據同步。同時,1.0版本架構中存在的讀多寫少的問題,通過個人域環的寫擴散來平衡讀寫壓力。

          下圖為一條消息從發送到接收的過程以及服務端和客戶端的執行流程:

          如上圖所示:假設Ua給Ub發送一條消息,消息寫擴散至Ua和Ub的各自的域環中:

          • 1)當客戶端online時,接收到推送的消息位點=當前端上域版本+1,本地消息數據庫merge即可;
          • 2)當客戶端offline時,僅進行離線推送通知,當用戶重新上線時,進行數據同步,由服務端判斷觸發增量同步還是全量同步。

          針對第2)點,具體邏輯是:

          • 1)如果域環版本差值小于閥值,增量同步后,進行本地消息數據庫merge;
          • 2)當域環版本差值大于閥值,進行全量消息拉取,做端上數據重建。

          整個同步邏輯基于閑魚的即時消息域環,域環可以看作是有著固定容量的用戶消息收件箱,給一個用戶發送的所有消息都會同步到他的域環中。

          具體就是:

          • 1)域環存儲:域環需要支持高并發數據讀寫,使用阿里分布式KV存儲系統tair來實現;
          • 2)域環容量:為減少全量消息同步,以用戶下次進入閑魚需要同步的平均消息量來規劃個人域環容量。同時利用FIFO循環覆蓋歷史數據;
          • 3)域環版本:用戶當前消息位點,在消息進入個人域環時通過tair的counter實現域環版本嚴格連續遞增,用于全量、增量同步判斷。

          上述建設完成后,閑魚具備了自己獨立的即時消息系統,當下遇到的問題得到了緩解,用戶體驗度有大幅提升。

          5、3.0版:隨著業務快速發展,系統穩定性需得到保障

          5.1 技術背景

          隨著閑魚業務生態的豐富,IM會話與消息內容類型不斷擴展,同時在用戶量的快速增長下,用戶反饋消息收不到、消息延遲等輿情問題日漸突出。

          5.2 問題分析

          問題1:閑魚app進程無有效保活機制,app退到后臺后進程很快就會被系統掛起,導致長連接中斷。此時消息推送走廠商通道,而廠商通道的實時性較差,且對消息推送的優先級設定有差異,從而造成用戶感知消息延遲。

          問題2:accs在線消息推送時,平均延時較短,但存在假連情況。而且目前的消息推送鏈路無ack機制,造成服務端以為消息發出去了但實際上客戶端并沒有收到,用戶下次打開app后才能看到消息,用戶感知消息延遲。

          PS:造成假連接的原因主要是用戶退到后臺,accs長連中斷,但是設備狀態更新有延時。

          問題3:目前消息同步的推模式(accs push)、拉模式(mtop),客戶端未做隔離,異步進行處理,導致在某些極端情況下消息數據庫處理異常,引發消息丟失。

          如:某用戶上線后連續收到多條消息,其中一條觸發域黑洞,在進行消息同步端上數據重建時,小概率處理出錯。

          問題4:大部分線上消息問題發現靠輿情反饋,如消息錯亂,出問題后系統無感知、無補救措施且排查困難,僅能跟隨版本做修復。

          問題5:業務不斷豐富,孵化出基于消息系統的服務號及小程序內容營銷、消息群組等,各類消息發送鏈路共用域環與數據存儲,造成穩定性問題。

          如:個人域環的消息包括IM聊天和營銷消息,IM聊天由用戶觸發,需要保證強到達;而營銷消息一般是由系統通過班車等方式批量發送,消息量級大,tps高,影響IM服務穩定性。

          5.3 解案決方案

          基于上述分析,我們逐個問題進行專項解決。

          1)消息重發與推拉隔離:

          如上圖所示:

          • a. ACK:保障消息及時到達。服務端下行accs消息時,將消息加入重試隊列并延遲重試,客戶端在收到accs消息并處理成功后,給服務端回一個ack,服務端收到ack后更新消息到達狀態,并終止重試,以此避免設備假連或網絡不穩定的情況;
          • b. 重發:根據延遲重發策略決定何時重發消息,保障消息確定性到達。自適應延遲重發策略是指新消息先通過4次固定N秒的短延遲來探測設備的網絡狀況,然后根據網絡狀況來遞增固定步長M的延遲策略,這種策略可以保障在最短的時間內,使用最少的重發次數將消息投遞成功;
          • c. 消息隊列:端上引入消息隊列,按順序處理消息,保證消息處理的準確性。同時進行推拉隔離,保障隊列有序消費,解決了復雜狀況下并發處理消息數據合并出錯的問題。

          2)數據存儲拆分:

          閑魚每天發送的即時消息中有一半以上是營銷消息,營銷消息的發送具有明顯的波峰波谷流量,高峰期會導致消息數據庫抖動,影響IM消息。我來對消息、摘要、域環存儲做業務隔離,以適應不同業務場景對穩定性不同的要求。

          具體做法是:

          • 1)IM消息需要極高的穩定性保證,其消息及摘要繼續使用mysql存儲;
          • 2)營銷消息存儲周期短,穩定性要求低于IM,采用Lindorm存儲;
          • 3)域環做實例級別隔離,保證IM域環的容量不會被其他消息占用,從而影響到消息同步。

          PS:Lindorm是一種多模型的云原生數據庫服務,具有成本低、自定義TTL、容量橫向擴展等優勢。

          3)線上問題發現與恢復:

          保障穩定性的關鍵要素是做好各種核心指標的監控,而監控首先要有數據來源,對服務端+客戶端的關鍵鏈路節點埋點,基于集團UT、SLS,通過blink進行實時清洗、計算,最終形成統一規范的日志數據落至SLS,以供實時監控及鏈路排查。

          消息系統的核心目標是保障用戶消息發的出、收得到且及時收到,所以我們通過計算發送成功率、到達率、消息延遲來監控系統的穩定性。

          此外,為了解決用戶輿情排查困難的問題:

          • 1)我們設計了一套指令集,通過約定指令協議,服務端向指定用戶下發指令,客戶端執行對應指令進行異常數據上報,提高排查效率;
          • 2)擴展了強制全量同步、數據校正等指令,定向修復用戶消息數據問題,相較以往出現嚴重bug只能讓用戶卸載重裝解決,這種方式顯然對用戶是更友好的。

          經過一系列專項治理,技術類輿情下降50%,從0到1建設了消息穩定性體系,用戶體驗進一步提升。

          6、展望未來

          閑魚作為電商交易APP, 其中IM是交易的前置鏈路,IM的產品體驗極大影響用戶交易效率。

          前段時間進行用戶調研,從閑魚IM的NPS低于預期(NPS是用戶忠誠度衡量指標 = 推薦者%-貶損者%)。

          從用戶反饋來看:

          • 1)部分用戶對產品功能有較強烈的訴求,諸如消息搜索、分組等;
          • 2)大部分用戶對發送消息過程中的違規問題難以理解;
          • 3)仍有較多輿情反饋消息收不到或延遲。

          映射到目前閑魚的即時消息系統上,我們的系統架構依然有很多需要持續改進的地方。

          典型的如:同步協議冗余,在需求迭代過程中容易引發問題、有效保活機制的缺失對消息即時送達的影響、小眾機型離線消息收不到、多年的數據積累在線庫臃腫等問題,影響著閑魚業務迭代速度與NPS。

          作為技術團隊,下一步將提升NPS作為核心技術目標,閑魚的即時消息系統4.0版架構正在路上 ......

          附錄:更多相關文章

          [1] 更多阿里巴巴的技術資源:

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

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

          阿里技術分享:深度揭秘阿里數據庫技術方案的10年變遷史

          阿里技術分享:阿里自研金融級數據庫OceanBase的艱辛成長之路

          來自阿里OpenIM:打造安全可靠即時通訊服務的技術實踐分享

          釘釘——基于IM技術的新一代企業OA平臺的技術挑戰(視頻+PPT) [附件下載]

          阿里技術結晶:《阿里巴巴Java開發手冊(規約)-華山版》[附件下載]

          重磅發布:《阿里巴巴Android開發手冊(規約)》[附件下載]

          作者談《阿里巴巴Java開發手冊(規約)》背后的故事

          《阿里巴巴Android開發手冊(規約)》背后的故事

          干了這碗雞湯:從理發店小弟到阿里P10技術大牛

          揭秘阿里、騰訊、華為、百度的職級和薪酬體系

          淘寶技術分享:手淘億級移動端接入層網關的技術演進之路

          難得干貨,揭秘支付寶的2維碼掃碼技術優化實踐之路

          淘寶直播技術干貨:高清、低延時的實時視頻直播技術解密

          阿里技術分享:電商IM消息平臺,在群聊、直播場景下的技術實踐

          阿里技術分享:閑魚IM基于Flutter的移動端跨端改造實踐

          阿里IM技術分享(三):閑魚億級IM消息系統的架構演進之路

           

          [2] 有關IM架構設計的文章:

          淺談IM系統的架構設計

          簡述移動端IM開發的那些坑:架構設計、通信協議和客戶端

          一套海量在線用戶的移動端IM架構設計實踐分享(含詳細圖文)

          一套原創分布式即時通訊(IM)系統理論架構方案

          從零到卓越:京東客服即時通訊系統的技術架構演進歷程

          蘑菇街即時通訊/IM服務器開發之架構選擇

          騰訊QQ1.4億在線用戶的技術挑戰和架構演進之路PPT

          微信后臺基于時間序的海量數據冷熱分級架構設計實踐

          微信技術總監談架構:微信之道——大道至簡(演講全文)

          如何解讀《微信技術總監談架構:微信之道——大道至簡》

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

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

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

          微信朋友圈千億訪問量背后的技術挑戰和實踐總結

          子彈短信光鮮的背后:網易云信首席架構師分享億級IM平臺的技術實踐

          微信技術分享:微信的海量IM聊天消息序列號生成實踐(算法原理篇)

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

          社交軟件紅包技術解密(一):全面解密QQ紅包技術方案——架構、技術實現等

          從游擊隊到正規軍(一):馬蜂窩旅游網的IM系統架構演進之路

          從游擊隊到正規軍(二):馬蜂窩旅游網的IM客戶端架構演進和實踐總結

          從游擊隊到正規軍(三):基于Go的馬蜂窩旅游網分布式IM系統技術實踐

          瓜子IM智能客服系統的數據架構設計(整理自現場演講,有配套PPT)

          IM開發基礎知識補課(九):想開發IM集群?先搞懂什么是RPC!

          阿里技術分享:電商IM消息平臺,在群聊、直播場景下的技術實踐

          一套億級用戶的IM架構技術干貨(上篇):整體架構、服務拆分等

          一套億級用戶的IM架構技術干貨(下篇):可靠性、有序性、弱網優化等

          從新手到專家:如何設計一套億級消息量的分布式IM系統

          企業微信的IM架構設計揭秘:消息模型、萬人群、已讀回執、消息撤回等

          融云技術分享:全面揭秘億級IM消息的可靠投遞機制

          IM開發技術學習:揭秘微信朋友圈這種信息推流背后的系統設計

          阿里IM技術分享(三):閑魚億級IM消息系統的架構演進之路

          >> 更多同類文章 ……

          本文已同步發布于“即時通訊技術圈”公眾號。

          同步發布鏈接是:http://www.52im.net/thread-3699-1-1.html 

          posted @ 2021-09-13 15:12 Jack Jiang 閱讀(328) | 評論 (0)編輯 收藏

               摘要: 本文引用自“ 豆米博客”的《JS實時通信三把斧》系列文章,有優化和改動。1、引言有關Web端即時通訊技術的文章我已整理過很多篇,閱讀過的讀者可能都很熟悉,早期的Web端即時通訊方案,受限于Web客戶端的技術限制,想實現真正的“即時”通信,難度相當大。傳統的Web端即時通訊技術從短輪詢到長連詢,再到Comet技術,在如此原始的HTML標準之下,為了實現...  閱讀全文

          posted @ 2021-09-07 10:47 Jack Jiang 閱讀(192) | 評論 (0)編輯 收藏

          本文由融云技術團隊原創分享,原題“技術實踐丨萬人群聊的消息分發控速方案”,為使文章更好理解,內容有修訂。

          1、引言

          傳統意義上的IM群聊,通常都是像微信這樣的500人群,或者QQ的2000人群(QQ有3000人群,但那是單獨收費的,也就意味著它并非無門檻標配,能用上的人并不多)。

          自從國外某號稱“世界上最安全的IM”搞出萬人群聊之后,萬人群迅速被國內的使用者們接受。伴隨著移動互聯網的發展,即時通訊服務被廣泛應用于各個行業(以經不再局限于傳統IM社交應用領域),隨著業務快速發展,傳統百人、千人上限的群聊已經無法滿足很多業務場景需求,所以萬人甚至十萬人的超大群也算是相伴而生、順應潮流。 

          ▲ “紙飛機”的萬人群(開發人員顫抖中...)

          IM群聊一直是IM應用中比較有難度的熱點技術之一,通常意義的群聊,無非就是500人群、1000人群、2000人群這樣,技術實現上比單聊要復雜不少。然而對于萬人群聊(甚至十萬人群聊)來說,相比百人、千人群聊,技術實現上那幾乎是另一個技術維度的事情,難度要高很多。

          本文根據融云技術團隊的實踐經驗,總結了萬人群聊消息投遞方案的一些思考和實踐,希望能給你帶來啟發。

          學習交流:

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

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

          - 開源IM框架源碼:https://github.com/JackJiang2011/MobileIMSDK

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

          2、相關文章

          萬人群聊有關的技術文章還可讀一讀以下這篇:

          1. 網易云信技術分享:IM中的萬人群聊技術方案實踐總結
          2. 企業微信的IM架構設計揭秘:消息模型、萬人群、已讀回執、消息撤回等
          3. 阿里釘釘技術分享:企業級IM王者——釘釘在后端架構上的過人之處

          融云技術團隊分享的其它文章:

          1. 融云技術分享:融云安卓端IM產品的網絡鏈路保活技術實踐
          2. 融云技術分享:全面揭秘億級IM消息的可靠投遞機制
          3. 融云技術分享:基于WebRTC的實時音視頻首幀顯示時間優化實踐
          4. IM消息ID技術專題(三):解密融云IM產品的聊天消息ID生成策略

          3、超大群面臨的技術挑戰

          與百人群、千人群相比,萬人、甚至十萬人超大群,大幅提升了群的觸達人數,對于很多業務場景來說,好處不言而喻。

          然而單群成員如此之大,給 IM 系統的流量沖擊非常巨大,技術難度可想而之。我們先來分析一下超大群的技術挑戰。

          以一個萬人群的模型為例:

          • 1)如果群中有人發了消息,那么這條消息需要按照 1:9999 的比例進行分發投遞,如果我們按照常規消息的處理流程,那么消息處理服務壓力巨大;
          • 2)消息量大的情況下,服務端向客戶端直推消息的處理速度將會成為系統瓶頸,而一旦用戶的消息下發隊列造成了擠壓,會影響到正常的消息分發,也會導致服務緩存使用量激增;
          • 3)在微服務架構中,服務以及存儲(DB,緩存)之間的 QPS 和網絡流量也會急劇增高;
          • 4)以群為單位的消息緩存,內存和存儲開銷較大(消息體的存儲被放大了萬倍)。

          基于這些技術挑戰,要想真正達成超大群的技術目標,勢必要做特定的技術優化來應對。

          4、一般群聊的消息投遞模型

          先來看看普通群聊的消息投遞模型。

          我們的普通群聊消息投遞模型如下圖所示:

          如上圖所示,當用戶在普通群里發了一條消息后,投遞路徑是:

          • 1)消息先到群組服務;
          • 2)然后通過群組服務緩存的群關系,鎖定這條消息最終需要分發的目標用戶;
          • 3)再根據一定的策略分發到消息服務上;
          • 4)消息服務再根據用戶的在線狀態和消息狀態來判斷這條消息是直推、通知拉取還是轉 Push,最終投遞給目標用戶。

          普通群聊的消息投遞,正像您期待的那樣,基本上大家的實現手段都大差不差。然而對于萬人群來說,這顯然還不夠。

          下面來看看我們針對萬人群聊消息投遞的技術優化手段。

          5、萬人群聊消息投遞優化手段1:控速

          針對萬人群的消息投遞,我們的一個主要手段就是控速。

          如上圖所示。

          首先:我們會根據服務器的核數來建立多個群消息分發隊列,這些隊列我們設置了不同的休眠時間以及不同的消費線程數。

          通俗來講,可以將隊列這樣劃分為快、中、慢等隊列。

          其次:我們根據群成員數量的大小來將所有群映射到相應的隊列中。

          規則是:

          • 1)小群映射到快隊列中;
          • 2)大群映射到相應的慢隊列中。

          然后:小群由于人數少,對服務的影響很小,所以服務利用快隊列快速的將群消息分發出去,而大群群消息則利用慢隊列的相對高延時來起到控速的作用。

          6、萬人群聊消息投遞優化手段2:合并

          在本文第3節中提到的萬人群聊所面臨的技術挑戰,最主要的挑戰其實就是消息進行擴散分發投遞后,消息被克隆出N條,消息流量瞬間被放大。

          舉個例子:當一條群消息發送到 IM 服務器后,需要從群組服務投遞給消息服務,如果每一個群成員都投遞一次,并且投遞的群消息內容是一致的話,那肯定會造成相應的資源浪費和服務壓力。

          那么針對這種情況,我們的解決方案就是進行消息合并投遞。

          原理就是:服務落點計算中我們使用的是一致性哈希,群成員落點相對固定,所以落點一致的群成員我們可以合并成一次請求進行投遞,這樣就大幅提高了投遞效率同時減少了服務的壓力。

          下圖是云信團隊分享的萬人群消息合并投遞邏輯:

          ▲ 上圖引用自《IM中的萬人群聊技術方案實踐總結

          如上圖所示,云信團隊的萬人群消息合并投遞方案是:按Link分組路由消息,同一Link上的全部群成員只需要路由一條消息即可。

          7、十萬、百萬級的超大群處理方案

          在實際群聊業務中,還有一種業務場景是超大規模群,這種群的群人數達到了數十萬甚至上百萬。

          這種群如果按照上述的投投遞方案,勢必仍會造成消息節點的巨大壓力。

          比如我們有一個十萬人的群,消息節點五臺,消息服務處理消息的上限是一秒鐘 4000 條,那每臺消息節點大約會分到 2 萬條群消息,這已大大超出了消息節點的處理能力。

          所以為了避免上述問題,我們會將群成員上線超過3000的群識別為萬人群、超級群,這種級別的群可以根據服務器數量和服務器配置相應做調整針對這種超級群會用特殊的隊列來處理群消息的投遞。

          這個特殊的隊列1秒鐘往后端消息服務投遞的消息數是消息服務處理上限的一半(留相應的能力處理其他消息),如果單臺消息服務處理的 QPS 上限是 4000,那群組服務一秒往單臺消息服務最多投遞 2000 條。

          8、寫在最后

          未來,我們也會針對群消息進行引用投遞,對于大群里發的消息體比較大的消息,我們給群成員只分發和緩存消息的索引,比如 MessageID。等群成員真正拉取群消息時再從將消息組裝好給客戶端分發下去。這樣做會節省分發的流量以及存儲的空間。

          隨著互聯網的發展,群組業務的模型和壓力也在不停地擴展,后續可能還會遇到更多的挑戰,當然也會不斷迭代出更優的處理方式來應對。

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

          直播系統聊天技術(一):百萬在線的美拍直播彈幕系統的實時推送技術實踐之路

          直播系統聊天技術(二):阿里電商IM消息平臺,在群聊、直播場景下的技術實踐

          直播系統聊天技術(三):微信直播聊天室單房間1500萬在線的消息架構演進之路

          直播系統聊天技術(四):百度直播的海量用戶實時消息系統架構演進實踐

          企業微信的IM架構設計揭秘:消息模型、萬人群、已讀回執、消息撤回等

          融云IM技術分享:萬人群聊消息投遞方案的思考和實踐

          >> 更多同類文章 ……

          本文已同步發布于“即時通訊技術圈”公眾號。

          同步發布鏈接是:http://www.52im.net/thread-3687-1-1.html

          posted @ 2021-08-30 11:34 Jack Jiang 閱讀(209) | 評論 (0)編輯 收藏

               摘要: 本文由微醫云技術團隊前端工程師張宇航分享,原題“從0到1打造一個 WebRTC 應用”,有修訂和改動。1、引言去年初,突如其來的新冠肺炎疫情讓線下就醫渠道幾乎被切斷,在此背景下,在線問診模式快速解決了大量急需就醫病患的燃眉之急。而作為在線問診中重要的一環——醫患之間的視頻問診正是應用了實時音視頻技術才得以實現。眾所周之,實時音視頻聊天技術門檻很高,一...  閱讀全文

          posted @ 2021-08-24 12:24 Jack Jiang 閱讀(316) | 評論 (0)編輯 收藏

          本文由徐寧發表于騰訊大講堂,原題“程序員如何把你關注的內容推送到你眼前?揭秘信息流推薦背后的系統設計”,有改動和修訂。

          1、引言

          信息推流(以下簡稱“Feed流”)這種功能在我們手機APP中幾乎無處不在(尤其是社交/社群產品中),最常用的就是微信朋友圈、新浪微博等。

          對Feed流的定義,可以簡單理解為只要大拇指不停地往下劃手機屏幕,就有一條條的信息不斷涌現出來。就像給牲畜喂飼料一樣,只要它吃光了就要不斷再往里加,故此得名Feed(飼養)。

          大多數帶有Feed流功能的產品都包含兩種Feed流:

          • 1)一種是基于算法:即動態算法推薦,比如今日頭條、抖音短視頻;
          • 2)一種是基于關注:即社交/好友關系,比如微信、知乎。

          例如下圖中的微博和知乎,頂欄的頁卡都包含“關注”和“推薦”這兩種:

          如上圖中這兩種Feed流,它們背后用到的技術差別會比較大。不同于“推薦”頁卡那種千人千面算法推薦的方式,通常“關注”頁卡所展示的內容先后順序都有固定的規則,最常見的規則是基于時間線來排序,也就是展示“我關注的人所發的帖子、動態、心情,根據發布時間從晚到早依次排列”。

          本文將重點討論的是“關注”功能對應的技術實現:先總結常用的基于時間線Feed流的后臺技術實現方案,再結合具體的業務場景,根據實際需求在基本設計思路上做一些靈活的運用。

          學習交流:

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

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

          - 開源IM框架源碼:https://github.com/JackJiang2011/MobileIMSDK

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

          2、本文作者

          徐寧:騰訊應用開發工程師,騰訊學院講師,畢業于上海交通大學。目前負責騰訊智慧零售業務的后端開發工作,有豐富的視頻直播,自動化營銷系統開發經驗。

          3、Feed流技術實現方案1:讀擴散

          讀擴散也稱為“拉模式”,這應該是最符合我們認知直覺的一種技術實現方式。

          原理如下圖:

          如上圖所示:每一個內容發布者都有一個自己的發件箱(“我發布的內容”),每當我們發出一個新帖子,都存入自己的發件箱中。當我們的粉絲來閱讀時,系統首先需要拿到粉絲關注的所有人,然后遍歷所有發布者的發件箱,取出他們所發布的帖子,然后依據發布時間排序,展示給閱讀者。

          這種設計:閱讀者讀一次Feed流,后臺會擴散為N次讀操作(N等于關注的人數)以及一次聚合操作,因此稱為讀擴散。每次讀Feed流相當于去關注者的收件箱主動拉取帖子,因此也得名——拉模式。

          這種模式:

          • 1)好處是:底層存儲簡單,沒有空間浪費;
          • 2)壞處是:每次讀操作會非常重,操作非常多。

          設想一下:如果我關注的人數非常多,遍歷一遍我所關注的所有人,并且再聚合一下,這個系統開銷會非常大,時延上可能達到無法忍受的地步。

          因此:讀擴散主要適用系統中閱讀者關注的人沒那么多,并且刷Feed流并不頻繁的場景。

          拉模式還有一個比較大的缺點:就是分頁不方便,我們刷微博或朋友圈,肯定是隨著大拇指在屏幕不斷劃動,內容一頁一頁的從后臺拉取。如果不做其他優化,只采用實時聚合的方式,下滑到比較靠后的頁碼時會非常麻煩。

          4、Feed流技術實現方案2:寫擴散

          據統計:大多數Feed流產品的讀寫比大概在100:1,也就是說大部分情況都是刷Feed流看別人發的朋友圈和微博,只有很少情況是自己親自發一條朋友圈或微博給別人看。

          因此:讀擴散那種很重的讀邏輯并不適合大多數場景。

          我們寧愿讓發帖的過程復雜一些,也不愿影響用戶讀Feed流的體驗,因此稍微改造一下前面方案就有了寫擴散。寫擴散也稱為“推模式”,這種模式會對拉模式的一些缺點做改進。

          原理如下圖:

          如上圖所示:系統中每個用戶除了有發件箱,也會有自己的收件箱。當發布者發表一篇帖子的時候,除了往自己發件箱記錄一下之外,還會遍歷發布者的所有粉絲,往這些粉絲的收件箱也投放一份相同內容。這樣閱讀者來讀Feed流時,直接從自己的收件箱讀取即可。

          這種設計:每次發表帖子,都會擴散為M次寫操作(M等于自己的粉絲數),因此成為寫擴散。每篇帖子都會主動推送到所有粉絲的收件箱,因此也得名推模式。

          這種模式可想而知:發一篇帖子,背后會涉及到很多次的寫操作。通常為了發帖人的用戶體驗,當發布的帖子寫到自己發件箱時,就可以返回發布成功。后臺另外起一個異步任務,不慌不忙地往粉絲收件箱投遞帖子即可。

          寫擴散的好處在于通過數據冗余(一篇帖子會被存儲M份副本),提升了閱讀者的用戶體驗。通常適當的數據冗余不是什么問題,但是到了微博明星這里,完全行不通。比如目前微博粉絲量Top2的謝娜與何炅,兩個人微博粉絲過億。

          設想一下:如果單純采用推模式,那每次謝娜何炅發一條微博,微博后臺都要地震一次。一篇微博導致后臺上億次寫操作,這顯然是不可行的。

          另外:由于寫擴散是異步操作,寫的太慢會導致帖子發出去半天,有些粉絲依然沒能看見,這種體驗也不太好。

          通常寫擴散適用于好友量不大的情況,比如微信朋友圈正是寫擴散模式。每一名微信用戶的好友上限為5000人,也就是說你發一條朋友圈最多也就擴散到5000次寫操作,如果異步任務性能好一些,完全沒有問題。

          關于微信朋友圈的技術資料:

          5、Feed流技術實現方案2:讀寫混合模式

          讀寫混合也可以稱作“推拉結合”,這種方式可以兼具讀擴散和寫擴散的優點。

          我們首先來總結一下讀擴散和寫擴散的優缺點:

          見上圖:仔細比較一下讀擴散與寫擴散的優缺點,不難發現兩者的適用場景是互補的。

          因此:在設計后臺存儲的時候,我們如果能夠區分一下場景,在不同場景下選擇最適合的方案,并且動態調整策略,就實現了讀寫混合模式。

          原理如下圖:

          以微博為例:當何炅這種粉絲量超大的人發帖時,將帖子寫入何炅的發件箱,另外提取出來何炅粉絲當中比較活躍的那一批(這已經可以篩掉大部分了),將何炅的帖子寫入他們的收件箱。當一個粉絲量很小的路人甲發帖時,采用寫擴散方式,遍歷他的所有粉絲并將帖子寫入粉絲收件箱。

          對于那些活躍用戶登錄刷Feed流時:他直接從自己的收件箱讀取帖子即可,保證了活躍用戶的體驗。

          當一個非活躍的用戶突然登錄刷Feed流時:

          • 1)一方面需要讀他的收件箱;
          • 2)另一面需要遍歷他所關注的大V用戶的發件箱提取帖子,并且做一下聚合展示。

          在展示完后:系統還需要有個任務來判斷是否有必要將該用戶升級為活躍用戶。

          因為有讀擴散的場景存在,因此即使是混合模式,每個閱讀者所能關注的人數也要設置上限,例如新浪微博限制每個賬號最多可以關注2000人。

          如果不設上限:設想一下有一位用戶把微博所有賬號全部關注了,那他打開關注列表會讀取到微博全站所有帖子,一旦出現讀擴散,系統必然崩潰(即使是寫擴散,他的收件箱也無法容納這么多的微博)。

          讀寫混合模式下,系統需要做兩個判斷:

          • 1)哪些用戶屬于大V,我們可以將粉絲量作為一個判斷指標;
          • 2)哪些用戶屬于活躍粉絲,這個判斷標準可以是最近一次登錄時間等。

          這兩處判斷標準就需要在系統發展過程中動態地識別和調整,沒有固定公式了。

          可以看出:讀寫結合模式綜合了兩種模式的優點,屬于最佳方案。

          然而他的缺點是:系統機制非常復雜,給程序員帶來無數煩惱。通常在項目初期,只有一兩個開發人員,用戶規模也很小的時候,一步到位地采用這種混合模式還是要慎重,容易出bug。當項目規模逐漸發展到新浪微博的水平,有一個大團隊專門來做Feed流時,讀寫混合模式才是必須的。

          6、Feed流中的分頁問題

          上面幾節已經敘述了基于時間線的幾種Feed流常見設計方案,但實操起來會比理論要麻煩許多。

          接下來專門討論一個Feed流技術方案中的痛點——Feed流的分頁。

          不管是讀擴散還是寫擴散,Feed流本質上是一個動態列表,列表內容會隨著時間不斷變化。傳統的前端分頁參數使用page_size和page_num,分表表示每頁幾條,以及當前是第幾頁。

          對于一個動態列表會有如下問題:

          如上圖所示:在T1時刻讀取了第一頁,T2時刻有人新發表了“內容11”,在T3時刻如果來拉取第二頁,會導致錯位出現,“內容6”在第一頁和第二頁都被返回了。事實上,但凡兩頁之間出現內容的添加或刪除,都會導致錯位問題。

          為了解決這一問題:通常Feed流的分頁入參不會使用page_size和page_num,而是使用last_id來記錄上一頁最后一條內容的id。前端讀取下一頁的時候,必須將last_id作為入參,后臺直接找到last_id對應數據,再往后偏移page_size條數據,返回給前端,這樣就避免了錯位問題。

          如下圖:

          采用last_id的方案有一個重要條件:就是last_id本身這條數據不可以被硬刪除。

          設想一下:

          • 1)上圖中T1時刻返回5條數據,last_id為內容6;
          • 2)T2時刻內容6被發布者刪除;
          • 3)那么T3時刻再來請求第二頁,我們根本找不到last_id對應的數據了,也就無法確認分頁偏移量。

          通常碰到刪除的場景:我們采用軟刪除方式,只是在內容上置一個標志位,表示內容已刪除。

          由于已經刪除的內容不應該再返回給前端,因此軟刪除模式下,找到last_id并往后偏移page_size條,如果其中有被刪除的數據會導致獲得足夠的數據條數給前端。

          這里一個解決方案是找不夠繼續再往下找,另一種方案是與前端協商,允許返回條數少于page_size條,page_size只是個建議值。甚至大家約定好了以后,可以不要page_size參數。

          7、Feed流技術方案在某直播應用中的實踐

          7.1 需求背景

          本節將結合實際業務,分享一下實際場景中碰到的一個非常特殊的Feed流設計方案。

          xx 直播是一款直播帶貨工具,主播可以創建一場未來時刻的直播,到時間后開播賣貨,直播結束后,主播的粉絲可以查看直播回放。

          這樣,每個直播場次就有三種狀態——預告中(創建一場直播但還未開播)、直播中、回放。

          作為觀眾,我可以關注多位主播,這樣從粉絲視角來看,也會有個直播場次的Feed流頁面。

          這個Feed流最特殊的地方在于它的排序規則:

          解釋一下這個Feed流排序規則:

          • 1)我關注的所有主播:正在直播中的場次排在最前;預告中的場次排中間;回放場次排最后;
          • 2)多場次都在直播中的:按開播時間從晚到早排序;
          • 3)多場次都在預告中的:按預計開播時間從早到晚排序;
          • 4)多場次都在回放的:按直播結束時間從晚到早排序。

          7.2 問題分析

          本需求最復雜的點在于Feed流內容融入的“狀態”因素,狀態的轉變會直接導致Feed流順序不同。

          為了更清晰解釋一下對排序的影響,我們可以用下圖詳細說明:

          上圖中:展示了4個主播的5個直播場次,作為觀眾,當我在T1時刻打開頁面,看到的順序是場次3在最上方,其余場次均在預告狀態,按照預計開播時間從早到晚展示。當我在T2時刻打開頁面,場次5在最上方,其余有三場在預告狀態排在中間,場次3已經結束了所以排在最后。以此類推,直到所有直播都結束,所有場次最終的狀態都會變為回放。

          這里需要注意一點:如果我在T1時刻打開第一頁,然后盯著頁面不動,一直盯到T4時刻再下劃到第二頁,這時上一頁的last_id,即分頁偏移量很有可能因為直播狀態變化而不知道飛到了什么位置,這會導致嚴重的錯位問題,以及直播狀態展示不統一的問題(第一頁展示的是T1時刻的直播狀態,第二頁展示的是T4時刻的直播狀態)。

          7.3 解決方案

          直播系統是個單向關系鏈,和微博有些類似,每個觀眾會關注少量主播,每個主播會可能有非常多的關注者。

          由于有狀態變化的存在,寫擴散幾乎無法實現。

          因為:如果采用寫擴散的方式,每次主播創建直播、直播開播、直播結束這三個事件發生時導致的場次狀態變化,會擴散為非常多次的寫操作,不僅操作復雜,時延上也無法接受。

          微博之所以可以寫擴散:就是因為一條微博發出后,這篇微博就不會再有任何影響排序的狀態轉變。

          而在我們場景中:“預告中”與“直播中”是兩個中間態,而“回放”狀態才是所有直播的最終歸宿,一旦進入回放,這場直播也就不會再有狀態轉變。因此“直播中”與“預告中”狀態可以采用讀擴散方式,“回放”狀態采取寫擴散方式。

          最終的方案如下圖所示:

          如上圖:會影響直播狀態的三種事件(創建直播、開播、結束直播)全部采用監聽隊列異步處理。

          我們為每一位主播維護一個直播中+預告中狀態的優先級隊列:

          • 1)每當監聽到有主播創建直播時,將直播場次加入隊列中,得分為開播的時間戳的相反數(負數);
          • 2)每當監聽到有主播開播時,把這場直播在隊列中的得分修改為開播時間(正數);
          • 3)每當監聽到有主播結束直播,則異步地將播放信息投遞到每個觀眾的回放隊列中。

          這里有一個小技巧:前文提到,直播中狀態按照開播時間從大到小排序,而預告中狀態則按照開播時間從小到大排序,因此如果將預告中狀態的得分全部取開播時間相反數,那排序同樣就成為了從大到小。這樣的轉化可以保證直播中與預告中同處于一個隊列排序。預告中得分全都為負數,直播中得分全都為正數,最后聚合時可以保證所有直播中全都自然排在預告中前面。

          另外:前文還提到的另一個問題是T1時刻拉取第一頁,T4時刻拉取第二頁,導致第一頁和第二頁直播間狀態不統一。

          解決這個問題的辦法是通過快照方式:當觀眾來拉取第一頁Feed流時,我們依據當前時間,將全部直播中和預告中狀態的場次建立一份快照,使用一個session_id標識,每次前端分頁拉取時,我們直接從快照中讀取即可。如果快照中讀取完畢,證明該觀眾的直播中和預告中場次全部讀完,剩下的則使用回放隊列進行補充。

          照此一來,我們的Feed流系統,前端分頁拉取的參數一共有4個:

          每當碰到session_id和last_id為空,則證明用戶想要讀取第一頁,需要重新構建快照。

          這里還有一個衍生問題:session_id的如何取值?

          答案是:

          • 1)如果不考慮同一個觀眾在多端登錄的情況,其實每一位觀眾維護一個快照id即可,也就是直接將系統用戶id設為session_id;
          • 2)如果考慮多端登錄的情況,則session_id中必須包含每個端的信息,以避免多端快照相互影響;
          • 3)如果不心疼內存,也可以每次隨機一個字符串作為session_id,并設置一個足夠長的過期時間,讓快照自然過期。

          以上設計:其實系統計算量最大的時刻就是拉取第一頁,構建快照的開銷。

          目前的線上數據,對于只關注不到10個主播的觀眾(這也是大多數場景),拉取第一頁的QPS可以達到1.5萬。如果將第二頁以后的請求也算進來,Feed流的綜合QPS可以達到更高水平,支撐目前的用戶規模已經綽綽有余。如果我們拉取第一頁時只獲取到前10條即可直接返回,將構建快照操作改為異步,也許QPS可以更高一些,這可能是后續的優化點。

          8、本文小結

          幾乎所有基于時間線和關注關系的Feed流都逃不開三種基本設計模式:

          • 1)讀擴散;
          • 2)寫擴散;
          • 3)讀寫混合。

          具體到實際業務中,可能會有更復雜的場景,比如本文所說的:

          • 1)狀態流轉影響排序;
          • 2)微博朋友圈場景中會有廣告接入、特別關注、熱點話題等可能影響到Feed流排序的因素。

          這些場景就只能根據業務需求,做相對應的變通了。

          附錄:更多社交應用架構設計文章

          1. 淺談IM系統的架構設計
          2. 簡述移動端IM開發的那些坑:架構設計、通信協議和客戶端
          3. 一套海量在線用戶的移動端IM架構設計實踐分享(含詳細圖文)
          4. 一套原創分布式即時通訊(IM)系統理論架構方案
          5. 從零到卓越:京東客服即時通訊系統的技術架構演進歷程
          6. 蘑菇街即時通訊/IM服務器開發之架構選擇
          7. 騰訊QQ1.4億在線用戶的技術挑戰和架構演進之路PPT
          8. 微信后臺基于時間序的海量數據冷熱分級架構設計實踐
          9. 微信技術總監談架構:微信之道——大道至簡(演講全文)
          10. 如何解讀《微信技術總監談架構:微信之道——大道至簡》
          11. 快速裂變:見證微信強大后臺架構從0到1的演進歷程(一)
          12. 移動端IM中大規模群消息的推送如何保證效率、實時性?
          13. 現代IM系統中聊天消息的同步和存儲方案探討
          14. 微信朋友圈千億訪問量背后的技術挑戰和實踐總結
          15. 以微博類應用場景為例,總結海量社交系統的架構設計步驟
          16. 子彈短信光鮮的背后:網易云信首席架構師分享億級IM平臺的技術實踐
          17. 知乎技術分享:從單機到2000萬QPS并發的Redis高性能緩存實踐之路
          18. 微信技術分享:微信的海量IM聊天消息序列號生成實踐(算法原理篇)
          19. 微信技術分享:微信的海量IM聊天消息序列號生成實踐(容災方案篇)
          20. 一套高可用、易伸縮、高并發的IM群聊、單聊架構方案設計實踐
          21. 社交軟件紅包技術解密(一):全面解密QQ紅包技術方案——架構、技術實現等
          22. 社交軟件紅包技術解密(二):解密微信搖一搖紅包從0到1的技術演進
          23. 社交軟件紅包技術解密(三):微信搖一搖紅包雨背后的技術細節
          24. 社交軟件紅包技術解密(四):微信紅包系統是如何應對高并發的
          25. 社交軟件紅包技術解密(五):微信紅包系統是如何實現高可用性的
          26. 社交軟件紅包技術解密(六):微信紅包系統的存儲層架構演進實踐
          27. 社交軟件紅包技術解密(七):支付寶紅包的海量高并發技術實踐
          28. 社交軟件紅包技術解密(八):全面解密微博紅包技術方案
          29. 社交軟件紅包技術解密(九):談談手Q紅包的功能邏輯、容災、運維、架構等
          30. 從游擊隊到正規軍(一):馬蜂窩旅游網的IM系統架構演進之路
          31. 從游擊隊到正規軍(二):馬蜂窩旅游網的IM客戶端架構演進和實踐總結
          32. 從游擊隊到正規軍(三):基于Go的馬蜂窩旅游網分布式IM系統技術實踐
          33. 瓜子IM智能客服系統的數據架構設計(整理自現場演講,有配套PPT)
          34. 阿里釘釘技術分享:企業級IM王者——釘釘在后端架構上的過人之處
          35. 微信后臺基于時間序的新一代海量數據存儲架構的設計實踐
          36. 阿里技術分享:電商IM消息平臺,在群聊、直播場景下的技術實踐
          37. 一套億級用戶的IM架構技術干貨(上篇):整體架構、服務拆分等
          38. 一套億級用戶的IM架構技術干貨(下篇):可靠性、有序性、弱網優化等
          39. 從新手到專家:如何設計一套億級消息量的分布式IM系統
          40. 企業微信的IM架構設計揭秘:消息模型、萬人群、已讀回執、消息撤回等
          41. 融云技術分享:全面揭秘億級IM消息的可靠投遞機制
          42. IM開發技術學習:揭秘微信朋友圈這種信息推流背后的系統設計
          43. >> 更多同類文章 ……

          本文已同步發布于“即時通訊技術圈”公眾號。

          同步發布鏈接是:http://www.52im.net/thread-3675-1-1.html

          posted @ 2021-08-17 15:34 Jack Jiang 閱讀(188) | 評論 (0)編輯 收藏

               摘要: 本文由美團技術團隊分享,作者“健午、佳猛、陸凱、馮江”,原題“美團終端消息投遞服務Pike的演進之路”,有修訂。1、引言傳統意義上來說,實時消息推送通常都是IM即時通訊這類應用的技術范疇,隨著移動端互聯網的普及,人人擁有手機、隨時都是“在線”已屬常態,于是消息的實時觸達能力獲得了廣泛的需求,已經不再局限于IM即時通訊這類應用中...  閱讀全文

          posted @ 2021-08-09 15:36 Jack Jiang 閱讀(242) | 評論 (0)編輯 收藏

          1、引言

          在IM客戶端的使用場景中,基于本地數據的全文檢索功能扮演著重要的角色,最常用的比如:查找聊天記錄、聯系人,就像下圖這樣。

          ▲ 微信的聊天記錄查找功能

          類似于IM中的聊天記錄查找、聯系人搜索這類功能,有了全文檢索能力后,確實能大大提高內容查找的效率,不然,讓用戶手動翻找,確實降低了用戶體驗。

          本文將具體來聊聊網易云信是如何實現IM客戶端全文檢索能力的,希望能帶給你啟發。

          學習交流:

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

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

          - 開源IM框架源碼:https://github.com/JackJiang2011/MobileIMSDK

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

          2、關于作者

          李寧:網易云信高級前端開發工程師,負責音視頻 IM SDK 的應用開發、組件化開發及解決方案開發,對 React、PaaS 組件化設計、多平臺的開發與編譯有豐富的實戰經驗。

          3、相關文章

          IM客戶端全文檢索相關文章:

          1. 微信手機端的本地數據全文檢索優化之路
          2. 微信團隊分享:微信移動端的全文檢索多音字問題解決方案

          網易技術團隊分享的其它文章:

          1. 網易視頻云技術分享:音頻處理與壓縮技術快速入門
          2. 網易云信實時視頻直播在TCP數據傳輸層的一些優化思路
          3. 網易云信技術分享:IM中的萬人群聊技術方案實踐總結
          4. Web端即時通訊實踐干貨:如何讓你的WebSocket斷網重連更快速?
          5. 子彈短信光鮮的背后:網易云信首席架構師分享億級IM平臺的技術實踐

          4、什么是全文檢索

          所謂全文檢索,就是要在大量內容中找到包含某個單詞出現位置的技術。

          在傳統的關系型數據庫中,只能通過 LIKE 條件查詢來實現,這樣有幾個弊端:

          • 1)無法使用數據庫索引,需要遍歷全表,性能較差;
          • 2)搜索效果差,只能首尾位模糊匹配,無法實現復雜的搜索需求;
          • 3)無法得到內容與搜索條件的相關性。

          我們在 IM 的 iOS、安卓以及桌面端中都實現了基于 SQLite 等庫的本地數據全文檢索功能,但是在 Web 端和 Electron 上缺少了這部分功能。

          因為在 Web 端,由于瀏覽器環境限制,能使用的本地存儲數據庫只有 IndexDB,暫不在討論的范圍內。但在 Electron 上,雖然也是內置了 Chromium 的內核,但是因為可以使用 Node.js 的能力,于是乎選擇的范圍就多了一些。本文內容我們具體以基于Electron的IM客戶端為例,來討論全文檢索技術實現(技術思路是相通的,并不局限于具體什么端)。

          PS:如果你不了解什么是Electron技術,讀一下這篇《快速了解Electron:新一代基于Web的跨平臺桌面技術》。

          我們先來具體看下該如何實現全文檢索。

          要實現全文檢索,離不開以下兩個知識點:

          • 1)倒排索引;
          • 2)分詞。

          這兩個技術是實現全文檢索的技術以及難點,其實現的過程相對比較復雜,在聊全文索引的實現前,我們具體學習一下這兩個技術的原理。

          5、全文檢索知識點1:倒排索引

          先簡單介紹下倒排索引,倒排索引的概念區別于正排索引:

          • 1)正排索引:是以文檔對象的唯一 ID 作為索引,以文檔內容作為記錄的結構;
          • 2)倒排索引:是以文檔內容中的單詞作為索引,將包含該詞的文檔 ID 作為記錄的結構。

          以倒排索引庫 search-index 舉個實際的例子:

          在我們的 IM 中,每條消息對象都有 idClient 作為唯一 ID,接下來我們輸入「今天天氣真好」,將其每個中文單獨分詞(分詞的概念我們在下文會詳細分享),于是輸入變成了「今」、「天」、「天」、「氣」、「真」、「好」。再通過 search-index 的 PUT 方法將其寫入庫中。

          最后看下上述例子存儲內容的結構:

          如是圖所示:可以看到倒排索引的結構,key 是分詞后的單個中文、value 是包含該中文消息對象的 idClient 組成的數組。

          當然:search-index 除了以上這些內容,還有一些其他內容,例如 Weight、Count 以及正排的數據等,這些是為了排序、分頁、按字段搜索等功能而存在的,本文就不再細細展開了。

          6、全文檢索知識點2:分詞

          6.1 基本概念

          分詞就是將原先一條消息的內容,根據語義切分成多個單字或詞句,考慮到中文分詞的效果以及需要在 Node 上運行,我們選擇了 Nodejieba 作為基礎分詞庫。

          以下是 jieba 分詞的流程圖:

          以“去北京大學玩”為例,我們選擇其中最為重要的幾個模塊分析一下。

          6.2 加載詞典

          jieba 分詞會在初始化時先加載詞典,大致內容如下:

          6.3 構建前綴詞典

          接下來會根據該詞典構建前綴詞典,結構如下:

          其中:“北京大”作為“北京大學”的前綴,它的詞頻是0,這是為了便于后續構建 DAG 圖。

          6.4 構建 DAG 圖

          DAG 圖是 Directed Acyclic Graph 的縮寫,即有向無環圖。

          基于前綴詞典,對輸入的內容進行切分。

          其中:

          • 1)“去”沒有前綴,因此只有一種切分方式;
          • 2)對于“北”,則有“北”、“北京”、“北京大學”三種切分方式;
          • 3)對于“京”,也只有一種切分方式;
          • 4)對于“大”,有“大”、“大學”兩種切分方式;
          • 5)對于“學”和“玩”,依然只有一種切分方式。

          如此,可以得到每個字作為前綴詞的切分方式。

          其 DAG 圖如下圖所示:

          6.5 最大概率路徑計算

          以上 DAG 圖的所有路徑如下:

          去/北/京/大/學/玩

          去/北京/大/學/玩

          去/北京/大學/玩

          去/北京大學/玩

          因為每個節點都是有權重(Weight)的,對于在前綴詞典里的詞語,它的權重就是它的詞頻。因此我們的問題就是想要求得一條最大路徑,使得整個句子的權重最高。

          這是一個典型的動態規劃問題,首先我們確認下動態規劃的兩個條件。

          1)重復子問題:

          對于節點 i 和其可能存在的多個后繼節點 j 和 k:

          • 1)任意通過i到達j的路徑的權重 = 該路徑通過i的路徑權重 + j的權重,即 R(i -> j) = R(i) + W(j);
          • 2)任意通過i到達k的路徑的權重 = 該路徑通過i的路徑權重 + k的權重,即 R(i -> k) = R(i) + W(k)。

          即對于擁有公共前驅節點 i 的 j 和 k,需要重復計算到達 i 路徑的權重。

          2)最優子結構:

          設整個句子的最優路徑為 Rmax,末端節點為 x,多個可能存在的前驅節點為 i、j、k。

          得到公式如下:

          Rmax = max(Rmaxi, Rmaxj, Rmaxk) + W(x)

          于是問題變成了求解 Rmaxi、Rmaxj 以及 Rmaxk,子結構里的最優解即是全局最優解的一部分。

          如上,最后計算得出最優路徑為“去/北京大學/玩”。

          6.6 HMM 隱式馬爾科夫模型

          對于未登陸詞,jieba 分詞采用 HMM(Hidden Markov Model 的縮寫)模型進行分詞。

          它將分詞問題視為一個序列標注問題,句子為觀測序列,分詞結果為狀態序列。

          jieba 分詞作者在 issue 中提到,HMM 模型的參數基于網上能下載到的 1998 人民日報的切分語料,一個 MSR 語料以及自己收集的 TXT 小說、用 ICTCLAS 切分,最后用 Python 腳本統計詞頻而成。

          該模型由一個五元組組成,并有兩個基本假設。

          五元組:

          • 1)狀態值集合;
          • 2)觀察值集合;
          • 3)狀態初始概率;
          • 4)狀態轉移概率;
          • 5)狀態發射概率。

          基本假設:

          • 1)齊次性假設:即假設隱藏的馬爾科夫鏈在任意時刻 t 的狀態只依賴于其前一時刻 t-1 的狀態,與其它時刻的狀態及觀測無關,也與時刻 t 無關;
          • 2)觀察值獨立性假設:即假設任意時刻的觀察值只與該時刻的馬爾科夫鏈的狀態有關,與其它觀測和狀態無關。

          狀態值集合即{ B: begin, E: end, M: middle, S: single },表示每個字所處在句子中的位置,B 為開始位置,E 為結束位置,M 為中間位置,S 是單字成詞。

          觀察值集合就是我們輸入句子中每個字組成的集合。

          狀態初始概率表明句子中的第一個字屬于 B、M、E、S 四種狀態的概率,其中 E 和 M 的概率都是0,因為第一個字只可能 B 或者 S,這與實際相符。

          狀態轉移概率表明從狀態 1 轉移到狀態 2 的概率,滿足齊次性假設,結構可以用一個嵌套的對象表示:

          P = {

              B: {E: -0.510825623765990, M: -0.916290731874155},

              E: {B: -0.5897149736854513, S: -0.8085250474669937},

              M: {E: -0.33344856811948514, M: -1.2603623820268226},

              S: {B: -0.7211965654669841, S: -0.6658631448798212},

          }

          P['B']['E'] 表示從狀態 B 轉移到狀態 E 的概率(結構中為概率的對數,方便計算)為 0.6,同理,P['B']['M'] 表示下一個狀態是 M 的概率為 0.4,說明當一個字處于開頭時,下一個字處于結尾的概率高于下一個字處于中間的概率,符合直覺,因為二個字的詞比多個字的詞要更常見。

          狀態發射概率表明當前狀態,滿足觀察值獨立性假設,結構同上,也可以用一個嵌套的對象表示:

          P = {

              B: {'突': -2.70366861046, '肅': -10.2782270947, '適': -5.57547658034},

              M: {'要': -4.26625051239, '合': -2.1517176509, '成': -5.11354837278},

              S: {……},

              E: {……},

          }

          P['B']['突'] 的含義就是狀態處于 B,觀測的字是“突”的概率的對數值等于 -2.70366861046。

          最后,通過 Viterbi 算法,輸入觀察值集合,將狀態初始概率、狀態轉移概率、狀態發射概率作為參數,輸出狀態值集合(即最大概率的分詞結果)。關于 Viterbi 算法,本文不再詳細展開,有興趣的讀者可以自行查閱。

          7、技術實現

          上節中介紹的全文檢索這兩塊技術,是我們架構的技術核心。基于此,我們對IM 的 Electron 端技術架構做了改進。以下將詳細介紹之。

          7.1 架構圖詳解

          考慮到全文檢索只是 IM 中的一個功能,為了不影響其他 IM 的功能,并且能更快的迭代需求,所以采用了如下的架構方案。

          架構圖如下:

          如上圖所示,右邊是之前的技術架構,底層存儲庫使用了 indexDB,上層有讀寫兩個模塊。

          讀寫模塊的具體作用是:

          • 1)當用戶主動發送消息、主動同步消息、主動刪除消息以及收到消息的時候,會將消息對象同步到 indexDB;
          • 2)當用戶需要查詢關鍵字的時候,會去 indexDB 中遍歷所有的消息對象,再使用 indexOf 判斷每一條消息對象是否包含所查詢的關鍵字(類似 LIKE)。

          那么,當數據量大的時候,查詢的速度是非常緩慢的。

          左邊是加入了分詞以及倒排索引數據庫的新的架構方案,這個方案不會對之前的方案有任何影響,只是在之前的方案之前加了一層。

          現在,讀寫模塊的工作邏輯:

          • 1)當用戶主動發送消息、主動同步消息、主動刪除消息以及收到消息的時候,會將每一條消息對象中的消息經過分詞后同步到倒排索引數據庫;
          • 2)當用戶需要查詢關鍵字的時候,會先去倒排索引數據庫中找出對應消息的 idClient,再根據 idClient 去 indexDB 中找出對應的消息對象返回給用戶。

          7.2 架構優點

          該方案有以下4個優點:

          • 1)速度快:通過 search-index 實現倒排索引,從而提升了搜索速度。
          • 2)跨平臺:因為 search-index 與 indexDB 都是基于 levelDB,因此 search-index 也支持瀏覽器環境,這樣就為 Web 端實現全文檢索提供了可能性;
          • 3)獨立性:倒排索引庫與 IM 主業務庫 indexDB 分離;
          • 4)靈活性:全文檢索以插件的形式接入。

          針對上述第“3)”點:當 indexDB 寫入數據時,會自動通知到倒排索引庫的寫模塊,將消息內容分詞后,插入到存儲隊列當中,最后依次插入到倒排索引數據庫中。當需要全文檢索時,通過倒排索引庫的讀模塊,能快速找到對應關鍵字的消息對象的 idClient,根據 idClient 再去 indexDB 中找到消息對象并返回。

          針對上述第“4)”點:它暴露出一個高階函數,包裹 IM 并返回新的經過繼承擴展的 IM,因為 JS 面向原型的機制,在新的 IM 中不存在的方法,會自動去原型鏈(即老的 IM)當中查找,因此,使得插件可以聚焦于自身方法的實現上,并且不需要關心 IM 的具體版本,并且插件支持自定義分詞函數,滿足不同用戶不同分詞需求的場景

          7.3 使用效果

          使用了如上架構后,經過我們的測試,在數據量 20W 的級別上,搜索時間從最開始的十幾秒降到一秒內,搜索速度快了 20 倍左右。

          8、本文小結

          本文中,我們便基于 Nodejieba 和 search-index 在 Electron 上實現了IM聊天消息的全文檢索,加快了聊天記錄的搜索速度。

          當然,后續我們還會針對以下方面做更多的優化,比如以下兩點:

          1)寫入性能 :在實際的使用中,發現當數據量大了以后,search-index 依賴的底層數據庫 levelDB 會存在寫入性能瓶頸,并且 CPU 和內存的消耗較大。經過調研,SQLite 的寫入性能相對要好很多,從觀測來看,寫入速度只與數據量成正比,CPU 和內存也相對穩定,因此,后續可能會考慮用將 SQLite 編譯成 Node 原生模塊來替換 search-index。

          2)可擴展性 :目前對于業務邏輯的解耦還不夠徹底。倒排索引庫當中存儲了某些業務字段。后續可以考慮倒排索引庫只根據關鍵字查找消息對象的 idClient,將帶業務屬性的搜索放到 indexDB 中,將倒排索引庫與主業務庫徹底解耦。

          以上,就是本文的全部分享,希望我的分享能對大家有所幫助。

          附錄:更多IM干貨技術文章

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

          從客戶端的角度來談談移動端IM的消息可靠性和送達機制

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

          移動端IM開發需要面對的技術問題

          IM消息送達保證機制實現(一):保證在線實時消息的可靠投遞

          IM消息送達保證機制實現(二):保證離線消息的可靠投遞

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

          一個低成本確保IM消息時序的方法探討

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

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

          談談移動端 IM 開發中登錄請求的優化

          移動端IM登錄時拉取數據如何作到省流量?

          淺談移動端IM的多點登錄和消息漫游原理

          完全自已開發的IM該如何設計“失敗重試”機制?

          通俗易懂:基于集群的移動端IM接入層負載均衡方案分享

          微信對網絡影響的技術試驗及分析(論文全文)

          微信技術分享:微信的海量IM聊天消息序列號生成實踐(算法原理篇)

          自已開發IM有那么難嗎?手把手教你自擼一個Andriod版簡易IM (有源碼)

          融云技術分享:解密融云IM產品的聊天消息ID生成策略

          適合新手:從零開發一個IM服務端(基于Netty,有完整源碼)

          拿起鍵盤就是干:跟我一起徒手開發一套分布式IM系統

          適合新手:手把手教你用Go快速搭建高性能、可擴展的IM系統(有源碼)

          IM里“附近的人”功能實現原理是什么?如何高效率地實現它?

          IM消息ID技術專題(一):微信的海量IM聊天消息序列號生成實踐(算法原理篇)

          IM開發寶典:史上最全,微信各種功能參數和邏輯規則資料匯總

          IM開發干貨分享:我是如何解決大量離線消息導致客戶端卡頓的

          零基礎IM開發入門(一):什么是IM系統?

          零基礎IM開發入門(二):什么是IM系統的實時性?

          零基礎IM開發入門(三):什么是IM系統的可靠性?

          零基礎IM開發入門(四):什么是IM系統的消息時序一致性?

          一套億級用戶的IM架構技術干貨(下篇):可靠性、有序性、弱網優化等

          IM掃碼登錄技術專題(三):通俗易懂,IM掃碼登錄功能詳細原理一篇就夠

          理解IM消息“可靠性”和“一致性”問題,以及解決方案探討

          阿里技術分享:閑魚IM基于Flutter的移動端跨端改造實踐

          融云技術分享:全面揭秘億級IM消息的可靠投遞機制

          IM開發干貨分享:如何優雅的實現大量離線消息的可靠投遞

          IM開發干貨分享:有贊移動端IM的組件化SDK架構設計實踐

          IM開發干貨分享:網易云信IM客戶端的聊天消息全文檢索技術實踐

          >> 更多同類文章 ……

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

          posted @ 2021-08-03 12:42 Jack Jiang 閱讀(295) | 評論 (0)編輯 收藏

          本文由融云技術團隊原創分享,原題“IM 消息同步機制全面解析”,為使文章更好理解,對內容進行了重新歸納和細節修訂。

          1、內容概述

          即時通訊(IM)系統最基礎、最重要的是消息的及時性與準確性,及時體現在延遲,準確則具體表現為不丟、不重、不亂序。

          綜合考慮業務場景、系統復雜度、網絡流量、終端能耗等,我們的億級分布式IM消息系統精心設計了消息收發機制,并不斷打磨優化,形成了現在的消息可靠投遞機制。

          整體思路就是:

          • 1)客戶端、服務端共同配合,互相補充;
          • 2)采用多重機制,從不同層面保障;
          • 3)拆分上下行,分別處理。

          本文根據融云億級IM消息系統的技術實踐,總結了分布式IM消息的可靠投遞機制,希望能為你的IM開發和知識學習起到拋磚引玉的作用。

          學習交流:

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

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

          - 開源IM框架源碼:https://github.com/JackJiang2011/MobileIMSDK

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

          2、推薦閱讀

          以下是相關文章匯總,有興趣可以一并閱讀:

          零基礎IM開發入門(二):什么是IM系統的實時性?

          零基礎IM開發入門(三):什么是IM系統的可靠性?

          零基礎IM開發入門(四):什么是IM系統的消息時序一致性?

          IM消息送達保證機制實現(一):保證在線實時消息的可靠投遞

          IM消息送達保證機制實現(二):保證離線消息的可靠投遞

          IM開發干貨分享:如何優雅的實現大量離線消息的可靠投遞

          理解IM消息“可靠性”和“一致性”問題,以及解決方案探討

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

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

          從客戶端的角度來談談移動端IM的消息可靠性和送達機制

          一套億級用戶的IM架構技術干貨(下篇):可靠性、有序性、弱網優化等

          從新手到專家:如何設計一套億級消息量的分布式IM系統

          淺談移動端IM的多點登錄和消息漫游原理

          完全自已開發的IM該如何設計“失敗重試”機制?

          IM開發干貨分享:我是如何解決大量離線消息導致客戶端卡頓的

          以下是融云技術團隊分享的其它文章:

          IM消息ID技術專題(三):解密融云IM產品的聊天消息ID生成策略

          融云技術分享:基于WebRTC的實時音視頻首幀顯示時間優化實踐

          融云技術分享:融云安卓端IM產品的網絡鏈路保活技術實踐

          即時通訊云融云CTO的創業經驗分享:技術創業,你真的準備好了?

          3、客戶端與服務端消息交互整體原理

          3.1 概述

          一個完整的IM消息交互邏輯,通常會為兩段:

          • 1)消息上行段:即由消息發送者通過IM實時通道發送給服務端;
          • 2)消息下行段:由服務端按照一定的策略送達給最終的消息接收人。

          3.2 消息上行段

          消息上行段主要就是依賴IM的實時通道將消息傳遞給服務端。

          這個階段的消息可靠投遞,需要從協議層進行保證,協議層需要提供可靠、有序的雙向字節流傳輸,我們是通過自研的通信協議 RMTP(即 RongCloud Message Transfer Protocol)實現的。

          客戶端與服務端之間使用長連接,基于 RMTP 協議傳輸數據。

          RMTP協議交互示意圖:

          如上圖所示,協議層通過 QoS、 ACK 等機制,保證IM消息上行段數據傳輸的可靠性。

          3.3 消息下行段

          經過總結,消息下行段主要有三種行為。

          1)客戶端主動拉取消息,主動拉取有兩個觸發方式:

          • ① 拉取離線消息:與 IM 服務新建立連接成功,用于獲取不在線的這段時間未收到的消息;
          • ② 定時拉取消息:在客戶端最后收到消息后啟動定時器,比如 3-5 分鐘執行一次。主要有兩個目的,一個是用于防止因網絡、中間設備等不確定因素引起的通知送達失敗,服務端客戶端狀態不一致,一個是可通過本次請求,對業務層做狀態機保活。

          2)服務端主動-發送消息(直發消息):

          這是在線消息發送機制之一,簡單理解為服務端將消息內容直接發送給客戶端,適用于消息頻率較低,并且持續交互,比如二人或者群內的正常交流討論。

          3)服務端主動-發送通知(通知拉取):

          這是在線消息發送機制之一,簡單理解為服務端給客戶端發送一個通知,通知包含時間戳等可作為排序索引的內容,客戶端收到通知后,依據自身數據,對比通知內時間戳,發起拉取消息的流程。

          這種場景適用于較多消息傳遞:比如某人有很多大規模的群,每個群內都有很多成員正在激烈討論。通過通知拉取機制,可以有效的減少客戶端服務端網絡交互次數,并且對多條消息進行打包,提升有效數據載荷。既能保證時效,又能保證性能。

          客戶端服務端下行段消息交互示意圖:

          4、客戶端與服務端消息交互具體實現

          正如上節所言,我們將消息的交互流程進行了拆分:即拆分出上下行。

          4.1 上行

          在上行過程保證發送消息順序,為了保證消息有序, 最好的方式是按照 userId 區分,然后使用時間戳排序。那么分布式部署情況下,將用戶歸屬到固定的業務服務器上(PS:指的是同一賬號的不同端固定連接到相同的業務服務器上),會使得上行排序變得更容易。同時歸屬到同一個服務器,在多端維護時也更容易。

          客戶端連接過程:

          • 1)客戶端通過 APP server ,獲取到連接使用的 token;
          • 2)客戶端使用 token 通過導航服務,獲取具體連接的 IM 接入服務器(CMP),導航服務通過 userId 計算接入服務器,然后下發,使得某一客戶端可以連接在同一臺接入服務器(CMP)。

          示意圖如下:

          小結一下就是:客戶端發出消息后,通過接入服務,按照 userId 投遞到指定消息服務器,生成消息 Id, 依據最后一條消息時間,確認更新當前消息的時間戳(如果存在相同時間戳則后延)。然后將時間戳,以及消息 Id,通過 Ack 返回給客戶端 ; 然后對上行消息使用 userId + 時間戳進行緩存以及持久化存儲,后續業務操作均使用此時間戳。

          以上業務流程我們稱為上行流程,上行過程存儲的消息為發件箱消息。

          PS:關于消息ID,需要補充說明一下:

          我們采用全局唯一的消息 ID 生成策略。保證消息可通過 ID 進行識別,排重。消息ID的結構如下圖所示。

          如何實現分布式場景下唯一 ID 生成,具體請閱讀:《IM消息ID技術專題(三):解密融云IM產品的聊天消息ID生成策略》。

          4.2 下行

          消息節點在處理完上行流程后,消息按照目標用戶投遞到所在消息節點,進入下行流程。

          下行過程,按照目標 userId 以及本消息在上行過程中生成的時間戳,計算是否需要更新時間戳(正向)。

          如果需要更新則對時間戳進行加法操作,直到當前用戶時間戳不重復。

          如此處理后,目標用戶的存儲以及客戶端接收到消息后的排重可以做到一致,并且可以做到同一個會話內的時間戳是有序的。從而保證同一個接收用戶的消息不會出現亂序。

          至此:我們已經介紹完了消息的下行交互過程,消息下行過程中的具體實現方式并不簡單,以下將詳細展開。

          1)直發消息:

          即服務端主動發送(給目標客戶端)的消息:

          • 1)客戶端 SDK 依據本地存儲的最新消息時間戳判斷,用來做排序等邏輯;
          • 2)對同一個用戶直發消息1條,其他轉通知。通知拉取時候客戶端選擇本地最新一條消息時間戳作為開始拉取時間;
          • 3)在消息發送過程中,如果上一條消息發送流程未結束,下一條消息則不用直發(s_msg),而是用通知(s_ntf)。

          直發邏輯示意圖:

          2)通知拉取:

          即服務端主動發送通知(給目標客戶端):

          • 1)服務端在通知體中攜帶當前消息時間戳。投遞給客戶端;
          • 2)客戶端收到通知后,比對本地消息時間戳,選擇是否發拉取消息信令;
          • 3)服務端收到拉取消息信令后,以信令攜帶的時間戳為開始,查詢出消息列表(200 條或者 5M),并給客戶端應答;
          • 4)客戶端收到后,給服務端 ack,服務端維護狀態;
          • 5)客戶端拉取消息時使用的時間戳,是客戶端本地最新一條消息的時間戳。

          示意圖:

          在上圖中,3-7 步可能需要循環多次,有以下考慮:

          • a、客戶端一次收到的消息過多,應答體積過于龐大,傳輸過程對網絡質量要求更高, 因此按照數量以及消息體積分批次進行;
          • b、一次拉取到的消息過多,客戶端處理會占用大量資源,可能會有卡頓等,體驗較差。

          3)服務端直發消息與通知拉取切換邏輯:

          主要涉及到的是狀態機的更新。

          下面示意圖集成直發消息與通知拉取過程針對狀態機的更新:

          至此,消息收發的整個核心流程介紹完畢,余下的內容將介紹多端在線的消息同步處理。

          5、多端在線的消息同步

          多端按照消息的上下行兩個階段,同樣區分為發送方多端同步以及接收方多端同步。

          5.1 發送方多端同步

          在前面客戶端連接 IM 服務過程中(見本文 4.1節),我們已經將同一個用戶的多個客戶端匯聚在了同一臺服務,那么維護一個 userId 的多端就會變得很簡單。

          具體邏輯是:

          • 1)用戶多個終端鏈接成功后,發送一條消息,這個消息到達 CMP(IM 接入服務) 后,CMP 做基礎檢查,然后獲此用戶的其他終端連接;
          • 2)服務把客戶端上行的消息,封裝為服務端下行消息,直接投遞給用戶的其他客戶端。這樣完成了發送方的多端抄送,然后將這條消息投遞到 IM 服務。進入正常發送投遞流程。

          針對上面的第2)點,發送方的多端同步沒有經過 IM Server,這么做的好處是:

          • 1)比較快速;
          • 2)經過越少的服務節點,出問題的幾率越小。

          5.2 接收方多端同步

          具體邏輯是:

          • 1)IM 服務收到消息后,先判斷接收方的投遞范圍,這個范圍指的是接收方用戶的哪些的終端要接收消息;
          • 2)IM 服務將范圍以及當前消息,發送到 CMP,CMP 依據范圍,匹配接收方的終端,然后投遞消息。

          接收方多端消息同步范圍的應用場景,一般都是針對所有終端。

          但有一些特殊業務:比如我在 A 客戶端上,控制另外某個端的狀態,可能需要一些命令消息, 這時候需要這個作用范圍,針對性的投遞消息。

          到此,我們分享完了有關 IM 消息核心處理流程,通過層層拆解邏輯,提供了可靠的消息投遞機制。

          附錄:更多IM架構設計的文章

          淺談IM系統的架構設計

          簡述移動端IM開發的那些坑:架構設計、通信協議和客戶端

          一套海量在線用戶的移動端IM架構設計實踐分享(含詳細圖文)

          一套原創分布式即時通訊(IM)系統理論架構方案

          從零到卓越:京東客服即時通訊系統的技術架構演進歷程

          蘑菇街即時通訊/IM服務器開發之架構選擇

          騰訊QQ1.4億在線用戶的技術挑戰和架構演進之路PPT

          微信后臺基于時間序的海量數據冷熱分級架構設計實踐

          微信技術總監談架構:微信之道——大道至簡(演講全文)

          如何解讀《微信技術總監談架構:微信之道——大道至簡》

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

          17年的實踐:騰訊海量產品的技術方法論

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

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

          IM開發基礎知識補課(二):如何設計大量圖片文件的服務端存儲架構?

          IM開發基礎知識補課(三):快速理解服務端數據庫讀寫分離原理及實踐建議

          IM開發基礎知識補課(四):正確理解HTTP短連接中的Cookie、Session和Token

          WhatsApp技術實踐分享:32人工程團隊創造的技術神話

          微信朋友圈千億訪問量背后的技術挑戰和實踐總結

          王者榮耀2億用戶量的背后:產品定位、技術架構、網絡方案等

          IM系統的MQ消息中間件選型:Kafka還是RabbitMQ?

          騰訊資深架構師干貨總結:一文讀懂大型分布式系統設計的方方面面

          以微博類應用場景為例,總結海量社交系統的架構設計步驟

          快速理解高性能HTTP服務端的負載均衡技術原理

          子彈短信光鮮的背后:網易云信首席架構師分享億級IM平臺的技術實踐

          知乎技術分享:從單機到2000萬QPS并發的Redis高性能緩存實踐之路

          IM開發基礎知識補課(五):通俗易懂,正確理解并用好MQ消息隊列

          微信技術分享:微信的海量IM聊天消息序列號生成實踐(算法原理篇)

          微信技術分享:微信的海量IM聊天消息序列號生成實踐(容災方案篇)

          新手入門:零基礎理解大型分布式架構的演進歷史、技術原理、最佳實踐

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

          阿里技術分享:深度揭秘阿里數據庫技術方案的10年變遷史

          阿里技術分享:阿里自研金融級數據庫OceanBase的艱辛成長之路

          社交軟件紅包技術解密(一):全面解密QQ紅包技術方案——架構、技術實現等

          社交軟件紅包技術解密(二):解密微信搖一搖紅包從0到1的技術演進

          社交軟件紅包技術解密(三):微信搖一搖紅包雨背后的技術細節

          社交軟件紅包技術解密(四):微信紅包系統是如何應對高并發的

          社交軟件紅包技術解密(五):微信紅包系統是如何實現高可用性的

          社交軟件紅包技術解密(六):微信紅包系統的存儲層架構演進實踐

          社交軟件紅包技術解密(七):支付寶紅包的海量高并發技術實踐

          社交軟件紅包技術解密(八):全面解密微博紅包技術方案

          社交軟件紅包技術解密(九):談談手Q紅包的功能邏輯、容災、運維、架構等

          社交軟件紅包技術解密(十):手Q客戶端針對2020年春節紅包的技術實踐

          社交軟件紅包技術解密(十一):解密微信紅包隨機算法(含代碼實現)

          即時通訊新手入門:一文讀懂什么是Nginx?它能否實現IM的負載均衡?

          即時通訊新手入門:快速理解RPC技術——基本概念、原理和用途

          多維度對比5款主流分布式MQ消息隊列,媽媽再也不擔心我的技術選型了

          從游擊隊到正規軍(一):馬蜂窩旅游網的IM系統架構演進之路

          從游擊隊到正規軍(二):馬蜂窩旅游網的IM客戶端架構演進和實踐總結

          從游擊隊到正規軍(三):基于Go的馬蜂窩旅游網分布式IM系統技術實踐

          IM開發基礎知識補課(六):數據庫用NoSQL還是SQL?讀這篇就夠了!

          瓜子IM智能客服系統的數據架構設計(整理自現場演講,有配套PPT)

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

          微信后臺基于時間序的新一代海量數據存儲架構的設計實踐

          IM開發基礎知識補課(九):想開發IM集群?先搞懂什么是RPC!

          阿里技術分享:電商IM消息平臺,在群聊、直播場景下的技術實踐

          一套億級用戶的IM架構技術干貨(上篇):整體架構、服務拆分等

          一套億級用戶的IM架構技術干貨(下篇):可靠性、有序性、弱網優化等

          從新手到專家:如何設計一套億級消息量的分布式IM系統

          企業微信的IM架構設計揭秘:消息模型、萬人群、已讀回執、消息撤回等

          融云技術分享:全面揭秘億級IM消息的可靠投遞機制

          >> 更多同類文章 ……

          本文已同步發布于“即時通訊技術圈”公眾號。

          ▲ 本文在公眾號上的鏈接是:點此進入。同步發布鏈接是:http://www.52im.net/thread-3638-1-1.html

          posted @ 2021-07-26 15:20 Jack Jiang 閱讀(154) | 評論 (0)編輯 收藏

          一、更新內容簡介

          本次為主要版本更新(本次更新內容見文末“MobileIMSDK v6.0更新內容 ”一節),強勢升級,將同時支持TCP、UDP、WebSocket三種協議,精心封裝之下,實現同一套API、三種協議同時并存。

          可能是市面上唯一同時支持UDP+TCP+WebSocket三種協議的同類開源IM框架。

          二、MobileIMSDK簡介

          MobileIMSDK 是一套專為移動端開發的原創IM通信層框架:

          • 歷經8年、久經考驗;
          • 超輕量級、高度提煉,lib包50KB以內;
          • 精心封裝,一套API同時支持UDP、TCP、WebSocket三種協議(可能是全網唯一開源的);
          • 客戶端支持 iOS、Android、標準Java、H5、小程序(開發中..)、Uniapp(開發中..);
          • 服務端基于Netty,性能卓越、易于擴展;??
          • 可與姊妹工程 MobileIMSDK-Web 無縫互通實現網頁端聊天或推送等;??
          • 可應用于跨設備、跨網絡的聊天APP、企業OA、消息推送等各種場景。

          MobileIMSDK工程始于2013年10月,起初用作某產品的即時通訊底層實現,完全從零開發,技術自主可控!

          您可能需要:查看關于MobileIMSDK的詳細介紹

          三、代碼托管同步更新

          OsChina.net

          GitHub.com

          四、MobileIMSDK設計目標

          讓開發者專注于應用邏輯的開發,底層復雜的即時通訊算法交由SDK開發人員,從而解偶即時通訊應用開發的復雜性。

          五、MobileIMSDK框架組成

          整套MobileIMSDK框架由以下5部分組成:

          1. Android客戶端SDK:用于Android版即時通訊客戶端,支持Android 2.3及以上,查看API文檔
          2. iOS客戶端SDK:用于開發iOS版即時通訊客戶端,支持iOS 8.0及以上,查看API文檔
          3. Java客戶端SDK:用于開發跨平臺的PC端即時通訊客戶端,支持Java 1.6及以上,查看API文檔
          4. H5客戶端SDK:資料整理中,不日正式發布;
          5. 服務端SDK:用于開發即時通訊服務端,支持Java 1.7及以上版本,查看API文檔

          六、MobileIMSDK v6.0更新內容 

          【重要說明】:

          MobileIMSDK v6 為全新版本,新增了對WebSocket協議的優雅支持、多端互踢支持等! 查看詳情

          【新增重要特性】:

          1. 服務端新增WebSocket協議支持,一套API優雅支持TCP、UDP、WebSocket 3種協議;
          2. 支持多端互踢功能(可應對復雜的移動端網絡變動邏輯對多端互踢算法的影響);

          【解決的Bug】:

          1. [Andriod]解決了斷線后,fireDisconnectedToServer()方法中的一處空指針隱患;
          2. [iOS] 修復了TCP版代碼中,調用[ClientCoreSDK releaseCore]方法會觸發自動登陸邏輯的bug;
          3. [服務端] 解決了UDP協議下,重連情況下的被踢者已被服務端注銷會話后,客戶端才發回登陸響應ACK應答,導致服務端錯誤地向未被踢者發出已登陸者重復登陸響應的問題;

          【其它優化和提升】:

          1. [Andriod]廢棄了SDK、Demo代碼中的所有AsyncTask的使用;
          2. [Andriod]將所有可使用Lambda表達式的代碼全部用Lambda進行了簡化。
          3. [iOS] 解決了XCode12上編譯SDK的.a包,打包成胖子.a時報“have the same architectures (arm64) and can't be in the same fat output file”的問題;
          4. [iOS] Demo中所有使用過時的UIAlertView改為UIAlertController實現;
          5. [iOS] 解決了iOS端SDK工程中兩處因類名重構導致的在XCode12.5.1上編譯出錯。
          6. [服務端] 將服務端Demo中的Log4j日志框架升級為最新的Log4j2;
          7. [服務端] 服務端可控制是否為每條消息生成發送時間戳(可輔助用于客戶端的消息排序邏輯等)。

          七、相關鏈接

          posted @ 2021-07-22 16:02 Jack Jiang 閱讀(165) | 評論 (0)編輯 收藏

               摘要: 本文作者潘唐磊,騰訊WXG(微信事業群)開發工程師,畢業于中山大學。內容有修訂。1、內容概述本文總結了企業微信的IM消息系統架構設計,闡述了企業業務給IM架構設計帶來的技術難點和挑戰,以及技術方案的對比與分析。同時總結了IM后臺開發的一些常用手段,適用于IM消息系統。* 推薦閱讀:企業微信團隊分享的另一篇《企業微信客戶端中組織架構數據的同步更新方案優化實戰》也值得一讀。學習交流:- 即時...  閱讀全文

          posted @ 2021-07-19 16:24 Jack Jiang 閱讀(217) | 評論 (0)編輯 收藏

          僅列出標題
          共50頁: First 上一頁 22 23 24 25 26 27 28 29 30 下一頁 Last 
          Jack Jiang的 Mail: jb2011@163.com, 聯系QQ: 413980957, 微信: hellojackjiang
          主站蜘蛛池模板: 阳朔县| 临海市| 阳春市| 韶山市| 房产| 牡丹江市| 汽车| 剑川县| 娄烦县| 师宗县| 孟连| 柘城县| 建湖县| 鄢陵县| 达孜县| 哈尔滨市| 分宜县| 浙江省| 清远市| 邹平县| 湖州市| 台南县| 凭祥市| 阿城市| 南开区| 阿合奇县| 岑巩县| 康定县| 四子王旗| 罗甸县| 南和县| 东平县| 张掖市| 丰县| 贵港市| 乌兰察布市| 丰都县| 图片| 平乡县| 白城市| 邵阳市|