Jack Jiang

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

          本文由喜馬拉雅技術團隊李乾坤原創,原題《推送系統實踐》,感謝作者的無私分享。

          1、引言

          1.1 什么是離線消息推送

          對于IM的開發者來說,離線消息推送是再熟悉不過的需求了,比如下圖就是典型的IM離線消息通知效果。

          1.2 Andriod端離線推送真心不易

          移動端離線消息推送涉及的端無非就是兩個——iOS端和Andriod端,iOS端沒什么好說的,APNs是唯一選項。

          Andriod端比較奇葩(主要指國內的手機),為了實現離線推送,各種保活黑科技層出不窮,隨著保活難度的不斷升級,可以使用的保活手段也是越來越少,有興趣可以讀一讀我整理的下面這些文章,感受一下(文章是按時間順序,隨著Andriod系統保活難度的提升,不斷進階的)。

          上面這幾篇只是我整理的這方面的文章中的一部分,特別注意這最后一篇《Android保活從入門到放棄:乖乖引導用戶加白名單吧(附7大機型加白示例)》。是的,當前Andriod系統對APP自已保活的容忍度幾乎為0,所以那些曾今的保活手段在新版本系統里,幾乎統統都失效了。

          自已做保活已經沒戲了,保離線消息推送總歸是還得做。怎么辦?按照現時的最佳實踐,那就是對接種手機廠商的ROOM級推送通道。具體我就不在這里展開,有興趣的地可以詳讀《Android P正式版即將到來:后臺應用保活、消息推送的真正噩夢》。

          自已做保活、自建推送通道的時代(這里當然指的是Andriod端啦),離線消息推送這種系統的架構設計相對簡單,無非就是每臺終端計算出一個deviceID,服務端通過自建通道進行消息透傳,就這么點事。

          而在自建通道死翹翹,只能依賴廠商推送通道的如今,小米華為魅族OPPOvivo(這只是主流的幾家)等等,手機型號太多,各家的推送API、設計規范各不相同(別跟我提什么統一推送聯盟,那玩意兒我等他3年了——詳見《萬眾矚目的“統一推送聯盟”上場了),這也直接導致先前的離線消息推送系統架構設計必須重新設計,以適應新時代的推送技術要求。

          1.3 怎么設計合理呢

          那么,針對不同廠商的ROOM級推送通道,我們的后臺推送架構到底該怎么設計合理呢?

          本文分享的離線消息推送系統設計并非專門針對IM產品,但無論業務層的差別有多少,大致的技術思路上都是相通的,希望借喜馬拉雅的這篇分享能給正在設計大用戶量的離線消息推送的你帶來些許啟發。

          * 推薦閱讀:喜馬拉雅技術團隊分享的另一篇《長連接網關技術專題(五):喜馬拉雅自研億級API網關技術實踐》,有興趣也可以一并閱讀。

          學習交流:

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

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

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

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

          2、技術背景

          首先介紹下在喜馬拉雅APP中推送系統的作用,如下圖就是一個新聞業務的推送/通知。

          離線推送主要就是在用戶不打開APP的時候有一個手段觸達用戶,保持APP的存在感,提高APP的日活。

          我們目前主要用推送的業務包括:

          • 1)主播開播:公司有直播業務,主播在開直播的時候會給這個主播的所有粉絲發一個推送開播提醒
          • 2)專輯更新:平臺上有非常多的專輯,專輯下面是一系列具體的聲音,比如一本兒小說是一個專輯,小說有很多章節,那么當小說更新章節的時候給所有訂閱這個專輯的用戶發一個更新的提醒:
          • 3)個性化、新聞業務等。

          既然想給一個用戶發離線推送,系統就要跟這個用戶設備之間有一個聯系的通道。

          做過這個的都知道:自建推送通道需要App常駐后臺(就是引言里提到的應用“保活”),而手機廠商因為省電等原因普遍采取“激進”的后臺進程管理策略,導致自建通道質量較差。目前通道一般是由“推送服務商”去維護,也就是說公司內的推送系統并不直接給用戶發推送(就是上節內容的這篇里提到的情況:《Android P正式版即將到來:后臺應用保活、消息推送的真正噩夢)。

          這種情況下的離線推送流轉流程如下:

          國內的幾大廠商(小米華為魅族OPPOvivo等)都有自己官方的推送通道,但是每一家接口都不一樣,所以一些廠商比如小米、個推提供集成接口。發送時推送系統發給集成商,然后集成商根據具體的設備,發給具體的廠商推送通道,最終發給用戶。

          給設備發推送的時候,必須說清楚你要發的是什么內容:即title、message/body,還要指定給哪個設備發推送。

          我們以token來標識一個設備, 在不同的場景下token的含義是不一樣的,公司內部一般用uid或者deviceId標識一個設備,對于集成商、不同的廠商也有自己對設備的唯一“編號”,所以公司內部的推送服務,要負責進行uid、deviceId到集成商token 的轉換。

          3、整體架構設計

          如上圖所示,推送系統整體上是一個基于隊列的流式處理系統。

          上圖右側:是主鏈路,各個業務方通過推送接口給推送系統發推送,推送接口會把數據發到一個隊列,由轉換和過濾服務消費。轉換就是上文說的uid/deviceId到token的轉換,過濾下文專門講,轉換過濾處理后發給發送模塊,最終給到集成商接口。

          App 啟動時:會向服務端發送綁定請求,上報uid/deviceId與token的綁定關系。當卸載/重裝App等導致token失效時,集成商通過http回調告知推送系統。各個組件都會通過kafka 發送流水到公司的xstream 實時流處理集群,聚合數據并落盤到mysql,最終由grafana提供各種報表展示。

          4、業務過濾機制設計

          各個業務方可以無腦給用戶發推送,但推送系統要有節制,因此要對業務消息有選擇的過濾。

          過濾機制的設計包括以下幾點(按支持的先后順序):

          • 1)用戶開關:App支持配置用戶開關,若用戶關閉了推送,則不向用戶設備發推送;
          • 2)文案排重:一個用戶不能收到重復的文案,用于防止上游業務方發送邏輯出錯;
          • 3)頻率控制:每一個業務對應一個msg_type,設定xx時間內最多發xx條推送;
          • 4)靜默時間:每天xx點到xx點不給用戶發推送,以免打擾用戶休息。
          • 5)分級管理:從用戶和消息兩維度進行分級控制。

          針對第5點,具體來說就是:

          • 1)每一個msg/msg_type有一個level,給重要/高level業務更多發送機會;
          • 2)當用戶一天收到xx條推送時,不是重要的消息就不再發給這些用戶。

          5、分庫分表下的多維查詢問題

          很多時候,設計都是基于理論和經驗,但實操時,總會遇到各種具體的問題。

          喜馬拉雅現在已經有6億+用戶,對應的推送系統的設備表(記錄uid/deviceId到token的映射)也有類似的量級,所以對設備表進行了分庫分表,以 deviceId 為分表列。

          但實際上:經常有根據 uid/token 的查詢需求,因此還需要建立以 uid/token 到 deviceId 的映射關系。因為uid 查詢的場景也很頻繁,因此uid副表也擁有和主表同樣的字段。

          因為每天會進行一兩次全局推,且針對沉默用戶(即不常使用APP的用戶)也有專門的推送,存儲方面實際上不存在“熱點”,雖然使用了緩存,但作用很有限,且占用空間巨大。

          多分表以及緩存導致數據存在三四個副本,不同邏輯使用不同副本,經常出現不一致問題(追求一致則影響性能), 查詢代碼非常復雜且性能較低。

          最終我們選擇了將設備數據存儲在tidb上,在性能夠用的前提下,大大簡化了代碼。

          6、特殊業務的時效性問題

          6.1 基本概念

          推送系統是基于隊列的,“先到先推”。大部分業務不要求很高的實時性,但直播業務要求半個小時送達,新聞業務更是“欲求不滿”,越快越好。

          若進行新聞推送時:隊列中有巨量的“專輯更新”推送等待處理,則專輯更新業務會嚴重干擾新聞業務的送達。

          6.2 這是隔離問題?

          一開始我們認為這是一個隔離問題:比如10個消費節點,3個專門負責高時效性業務、7個節點負責一般業務。當時隊列用的是rabbitmq,為此改造了 spring-rabbit 支持根據msytype將消息路由到特定節點。

          該方案有以下缺點:

          • 1)總有一些機器很忙的時候,另一些機器在“袖手旁觀”;
          • 2)新增業務時,需要額外配置msgType到消費節點的映射關系,維護成本較高;
          • 3)rabbitmq基于內存實現,推送瞬時高峰時占用內存較大,進而引發rabbitmq 不穩定。

          6.3 其實是個優先級問題

          后來我們覺察到這是一個優先級問題:高優先級業務/消息可以插隊,于是封裝kafka支持優先級,比較好的解決了隔離性方案帶來的問題。具體實現是建立多個topic,一個topic代表一個優先級,封裝kafka主要是封裝消費端的邏輯(即構造一個PriorityConsumer)。

          備注:為描述簡單,本文使用 consumer.poll(num) 來描述使用 consumer 拉取 num 個消息,與真實 kafka api 不一致,請知悉。

          PriorityConsumer實現有三種方案,以下分別闡述。

          1)poll到內存后重新排序:java 有現成的基于內存的優先級隊列PriorityQueue 或PriorityBlockingQueue,kafka consumer 正常消費,并將poll 到的數據重新push到優先級隊列。

          • 1.1)如果使用有界隊列,隊列打滿后,后面的消息優先級再高也put 不進去,失去“插隊”效果;
          • 1.2)如果使用無界隊列,本來應堆在kafka上的消息都會堆到內存里,OOM的風險很大。

          2)先拉取高優先級topic的數據:只要有就一直消費,直到沒有數據再消費低一級topic。消費低一級topic的過程中,如果發現有高一級topic消息到來,則轉向消費高優先級消息。

          該方案實現較為復雜,且在晚高峰等推送密集的時間段,可能會導致低優先級業務完全失去推送機會。

          3)優先級從高到低,循環拉取數據:

          一次循環的邏輯為:

          consumer-1.poll(topic1-num);

          cosumer-i.poll(topic-i-num);

          consumer-max.priority.poll(topic-max.priority-num)

          如果topic1-num=topic-i-num=topic-max.priority-num,則該方案是沒有優先級效果的。topic1-num 可以視為權重,我們約定:topic-高-num=2 * topic-低-num,同一時刻所有topic 都會被消費,通過一次消費數量的多少來變相實現“插隊效果”。具體細節上還借鑒了“滑動窗口”策略來優化某個優先級的topic 長期沒有消息時總的消費性能。

          從中我們可以看到,時效問題先是被理解為一個隔離問題,后被視為優先級問題,最終轉化為了一個權重問題。

          7、過濾機制的存儲和性能問題

          在我們的架構中,影響推送發送速度的主要就是tidb查詢和過濾邏輯,過濾機制又分為存儲和性能兩個問題。

          這里我們以xx業務頻控限制“一個小時最多發送一條”為例來進行分析。

          第一版實現時:redis kv 結構為 <deviceId_msgtype,已發送推送數量>

          頻控實現邏輯為:

          • 1)發送時,incr key,發送次數加1;
          • 2)如果超限(incr命令返回值>發送次數上限),則不推送;
          • 3)若未超限且返回值為1,說明在msgtype頻控周期內第一次向該deviceId發消息,需expire key設置過期時間(等于頻控周期)。

          上述方案有以下缺點:

          • 1)目前公司有60+推送業務, 6億+ deviceId,一共6億*60個key ,占用空間巨大;
          • 2)很多時候,處理一個deviceId需要2條指令:incr+expire。

          為此,我們的解決方法是:

          • 1)使用pika(基于磁盤的redis)替換redis,磁盤空間可以滿足存儲需求;
          • 2)委托系統架構組擴充了redis協議,支持新結構ehash。

          ehash基于redis hash修改,是一個兩級map <key,field,value>,除了key 可以設置有效期外,field也可以支持有效期,且支持有條件的設置有效期。

          頻控數據的存儲結構由<deviceId_msgtype,value>變為 <deviceId,msgtype,value>,這樣對于多個msgtype,deviceId只存一次,節省了占用空間。

          incr 和 expire 合并為1條指令:incr(key,filed,expire),減少了一次網絡通信:

          • 1)當field未設置有效期時,則為其設置有效期;
          • 2)當field還未過期時,則忽略有效期參數。

          因為推送系統重度使用 incr 指令,可以視為一條寫指令,大部分場景還用了pipeline 來實現批量寫的效果,我們委托系統架構組小伙伴專門優化了pika 的寫入性能,支持“寫模式”(優化了寫場景下的相關參數),qps達到10w以上。

          ehash結構在流水記錄時也發揮了重要作用,比如<deviceId,msgId,100001002>,其中 100001002 是我們約定的一個數據格式示例值,前中后三個部分(每個部分占3位)分別表示了某個消息(msgId)針對deviceId的發送、接收和點擊詳情,比如頭3位“100”表示因發送時處于靜默時間段所以發送失敗。

          附錄:更多消息推送技術文章

          iOS的推送服務APNs詳解:設計思路、技術原理及缺陷等

          信鴿團隊原創:一起走過 iOS10 上消息推送(APNS)的坑

          Android端消息推送總結:實現原理、心跳保活、遇到的問題等

          掃盲貼:認識MQTT通信協議

          一個基于MQTT通信協議的完整Android推送Demo

          IBM技術經理訪談:MQTT協議的制定歷程、發展現狀等

          求教android消息推送:GCM、XMPP、MQTT三種方案的優劣

          移動端實時消息推送技術淺析

          掃盲貼:淺談iOS和Android后臺實時消息推送的原理和區別

          絕對干貨:基于Netty實現海量接入的推送服務技術要點

          移動端IM實踐:谷歌消息推送服務(GCM)研究(來自微信)

          為何微信、QQ這樣的IM工具不使用GCM服務推送消息?

          極光推送系統大規模高并發架構的技術實踐分享

          從HTTP到MQTT:一個基于位置服務的APP數據通信實踐概述

          魅族2500萬長連接的實時消息推送架構的技術實踐分享

          專訪魅族架構師:海量長連接的實時消息推送系統的心得體會

          深入的聊聊Android消息推送這件小事

          基于WebSocket實現Hybrid移動應用的消息推送實踐(含代碼示例)

          一個基于長連接的安全可擴展的訂閱/推送服務實現思路

          實踐分享:如何構建一套高可用的移動端消息推送系統?

          Go語言構建千萬級在線的高并發消息推送系統實踐(來自360公司)

          騰訊信鴿技術分享:百億級實時消息推送的實戰經驗

          百萬在線的美拍直播彈幕系統的實時推送技術實踐之路

          京東京麥商家開放平臺的消息推送架構演進之路

          了解iOS消息推送一文就夠:史上最全iOS Push技術詳解

          基于APNs最新HTTP/2接口實現iOS的高性能消息推送(服務端篇)

          解密“達達-京東到家”的訂單即時派發技術原理和實踐

          技術干貨:從零開始,教你設計一個百萬級的消息推送系統

          長連接網關技術專題(四):愛奇藝WebSocket實時推送網關技術實踐

          喜馬拉雅億級用戶量的離線消息推送系統架構設計實踐

          >> 更多同類文章 ……

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

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

          posted @ 2021-07-12 15:31 Jack Jiang 閱讀(499) | 評論 (0)編輯 收藏

          本文由阿里閑魚技術團隊祈晴分享,本次有修訂和改動,感謝作者的技術分享。

          1、內容概述

          本文總結了阿里閑魚技術團隊使用Flutter在對閑魚IM進行移動端跨端改造過程中的技術實踐等,文中對比了傳統Native與現在大熱的Flutter跨端方案在一些主要技術實現上的差異,以及針對Flutter技術特點的具體技術實現,值得同樣準備使用Flutter開發IM的技術同行們借鑒和參考。

          學習交流:

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

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

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

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

          2、閑魚IM現狀

          閑魚IM的移動端框架構建于2016至2017年間,期間經過多次迭代升級導致歷史包袱累積多,后面又經歷IM界面的Flutter化,從而造成了客戶端架構愈加復雜。

          從開發層面總結閑魚IM移動端當前架構主要存在如下幾個問題:

          • 1)研發效率較低:當前架構涉及到Android/iOS雙端的邏輯代碼以及Flutter的UI代碼,定位問題往往只能從Flutter UI表相倒查到Native邏輯層;
          • 2)架構層次較差:架構設計上分層不清晰,業務邏輯夾雜在核心的邏輯層致使代碼變更風險大;
          • 3)性能測試略差:核心數據源存儲Native內存,需經Flutter Plugin將數據源序列化上拋Flutter側,在大批量數據源情況下性能表現較差。

          從產品層面總結閑魚IM移動端當前架構的主要問題如下:

          • 1)定位問題困難:線上輿情反饋千奇百怪,測試始終無法復現相關場景,因此很多時候只能靠現象猜測本質;
          • 2)疑難雜癥較多:架構的不穩定性造成出現的問題反復出現,當前疑難雜癥主要包括未讀紅點計數、iPhone5C低端機以及多媒體發送等多個問題;
          • 3)問題差異性大:Android和iOS兩端邏輯代碼差異大,包括埋點邏輯都不盡相同,排查問題根源時雙端都會有不同根因,解決方案也不相同。

          3、業界的移動端跨端方案

          為解決當前IM的技術痛點,閑魚今年特起關于IM架構升級項目,重在解決客戶端中Andriod和iOS雙端一致性的痛點,初步設想方案就是實現跨端統一的Android/iOS邏輯架構。

          在當前行業內跨端方案可初步歸類如下圖架構:

          在GUI層面的跨端方案有WeexReactNative、H5、Uni-APP等,其內存模型大多需要通過橋接到Native模式存儲。

          在邏輯層面的跨端方案大致有C/C++等與虛擬機無關語言實現跨端,當然匯編語言也可行。

          此外有兩個獨立于上述體系之外的架構就是Flutter和KMM(谷歌基于Kotlin實現類似Flutter架構),其中Flutter運行特定DartVM,將內存數據掛載其自身的isolate中。

          考慮閑魚是Flutter的前沿探索者,方案上優先使用Flutter。然而Flutter的isolate更像一個進程的概念(底層實現非使用進程模式),相比Android,同一進程場景中,Android的Dalvik虛擬機多個線程運行共享一個內存Heap,而DartVM的Isolate運行隔離各自的Heap,因而isolate之間通訊方式比較繁瑣(需經過序列化反序列化過程)。

          整個模型如下圖所示:

          若按官方混合架構實現Flutter應用,開啟多個FlutterAcitivty/FlutterController,底層會生成多個Engine,對應會存在多個isolate,而isolate通訊類似于進程通訊(類似socket或AIDL),這里借鑒閑魚FlutterBoost的設計理念,FlutterIM架構將多個頁面的Engine共享,則內存模型就天然支持共享讀取。

          原理圖如下:

          4、閑魚IM基于Flutter的架構設計

          4.1 新老架構對比

          如下圖所示:是一個老架構方案,其核心問題主要集中于Native邏輯抽象差,其中邏輯層面還設計到多線程并發使得問題倍增,Android/iOS/Flutter交互繁雜,開發維護成本高,核心層耦合較為嚴重,無插拔式概念.

          考慮到歷史架構的問題,演進如下新架構設計:

          如上圖所示,架構從上至下依次為:

          • 1)業務層;
          • 2)分發層;
          • 3)邏輯層;
          • 4)數據源層。

          數據源層來源于推送或網絡請求,其封裝于Native層,通過Flutter插件將消息協議數據上拋到Flutter側的核心邏輯層,處理完成后變成Flutter DB的Enitity實體,實體中掛載一些消息協議實體。

          核心邏輯層將繁雜數據扁平化打包掛載到分發層中的會話內存模型數據或消息內存模型數據,最后通過觀察者模式的訂閱分發到業務邏輯中。

          Flutter IM重點集中改造邏輯層和分發層,將IM核心邏輯和業務層面數據模型進行封裝隔離,核心邏輯層和數據庫交互后將數據封裝到分發層的moduleData中,通過訂閱方式分發到業務層數據模型中。

          此外在IM模型中DB也是重點依賴的,個人對DB數據庫管理進行全面封裝解,實現一種輕量級,性能佳的Flutter DB管理框架。

          4.2 DB存儲模型

          Flutter IM架構的DB存儲依賴數據庫插件,目前主流插件是Sqflite

          其存儲模型如下:

          依據上圖Sqflite插件的DB存儲模型會有2個等待隊列:

          • 一個是Flutter層同步執行隊列;
          • 一個是Native層的線程執行隊列。

          其Android實現機制是HandlerThread,因此Query/Save讀寫在會同一線程隊列中,導致響應速度慢,容易造成DB SQL堆積,此外缺失緩存模型。

          于是個人定制如下改進方案:

          Flutter側通過表的主鍵設計查詢時候會優先從Entity Cache層去獲取,若緩存不存在,則通過Sqflite插件查詢。

          同時改造Sqflite插件成支持sync/Async同步異步兩種方式操作,對應到Native側也會有同步線程隊列和異步線程隊列,保證數據吞吐率。但是這里建議查詢使用異步,存儲使用同步更穩妥,主要怕出現多個相同的數據元model同一時間進入異步線程池中,存儲先后順序無法有效的保證。

          4.3 ORM數據庫方案

          IM架構重度依賴DB數據庫,而當前業界還沒有一個完備的數據庫ORM管理方案,參考了Android的OrmLite/GreenDao,個人自行設計一套Flutter ORM數據庫管理方案。

          其核心思想如下:

          由于Flutter不支持反射,因此無法直接像Android的開源數據庫方式操作,但可通過APT方式,將Entity和Orm Entity綁定于一身,操作OrmEntity即操作Entity,整個代碼風格設計也和OrmLite極其相似。

          參考代碼如下:

          4.4 IM內存數據模型

          基于Flutter的IM移動端架構在內存數據模型主要劃分為會話和消息兩個顆粒度:

          • 1)會話內存數據模型交托于SessionModuleData:會話內存數據有一個根節點RootNotice,然后其掛載PSessionMessageNotice(這里PSessionMessageNotice是ORM映射的會話DB表模型)子節點集合。
          • 2)消息內存數據模型交托于MessageModuleData:消息內存數據會有一個MessageConatiner容器管理,其內部掛載此會話中的PMessage(PMessage是ORM映射的消息DB表模型)消息集合。

          依據上一章節,PSessionMessageNotice設計了一個OrmEnitity Cache,考慮到IM中會話數是有限的,因此PSessionMessageNotice都是直接緩存到Cache中。

          這種做法的好處是各地去拿會話數據元時候都是緩存中同一個對象,容易保證多次重復讀寫的數據一致性。而PSessionMessageNotice考慮到其數量可以無限多的特殊性,因此這里將其掛載到MessageContainer的內存管理中,在退出會話的時機會校驗容器中PMessage集合的數量,適當縮容可以減少內存開銷。

          模型如下圖所示:

          4.5 狀態管理方案

          基于Flutter的IM移動端架構狀態管理方案比較簡單,對數據源Session/Message維度使用觀察者模式的訂閱分發方式實現,架構類似于EventBus模式,頁面級的狀態管理無論使用fish-redux、scopeModel或者provider幾乎影響面不大,核心還是需保留一種插拔式抽象更重要。

          架構如下圖:

          4.6 IM同步模型方案

          當前現狀的消息同步模型:

          如上圖所示是,模型中存在ACCS Thread/Main Thread/Region Thread等多線程并發場景,導致易出現多線程高并發的問題。

          native的推送和網絡請求同步的隔離方案通過Lock的鎖機制,并且通過隊列降頻等方式處理,流程繁瑣且易出錯。整體通過Region Version Gap去判斷是否有域空洞,進而執行域同步補充數據。

          改進的同步模型如下:

          如上圖所示,在Flutter側天然沒多線程場景,通過一種標記位的轉化同步異步實現類似Handler消息隊列,架構清晰簡約了很多,避免鎖帶來的開銷以及同步問題。

          5、本次改造進展以及性能對比

          1)針對架構層面:

          在基于Flutter的IM架構中,重點將雙端邏輯差異性統一成同一份Dart代碼,完全磨平Android/iOS的代碼差異性帶來的問題。

          帶來的好處很明顯:

          • 1)降低開發維護、測試回歸、視覺驗收的一半成本,極大提高研發效率;
          • 2)架構上進行重構分層,實現一種解耦合,插拔式的IM架構;
          • 3)同時Native到Flutter側的大量數據上拋序列化過程改造程Flutter引用傳遞,解決極限測試場景下的私聊卡頓問題。

          2)針對線上輿情:

          • 1)補齊UT和TLog的集團日志方式做到可追蹤,可排查;
          • 2)針對于很多現存的疑難雜癥重點集中專項解決,比如iphone5C的架構在Flutter側統一規劃;
          • 3)未讀紅點計數等問題也在架構模型升級中修復;
          • 4)此外多媒體音視頻發送模塊進行改造升級。

          3)性能數據對比:

          當IM架構的邏輯層和UI層都切換成Flutter后,和原先架構模式初步對比,整體內存水位持平。

          其中:

          • 1)私聊場景下小米9測試結構內存下降40M,功耗降低4mah,CPU降低1%;
          • 2)極限測試場景下新架構內存數據相比于舊架構有一個較為明顯的改觀(主要由于兩個界面都使用Flutter場景下,頁面切換的開銷降低很多)。

          6、未來展望

          JS跨端不安全,C++跨端成本有點高,Flutter會是一個較好選擇。彼時閑魚FlutterIM架構升級根本目的從來不是因Flutter而Flutter,是由于歷史包袱的繁重,代碼層面的維護成本高,新業務的擴展性差,人力配比不協調以及疑難雜癥的輿情持續反饋等等因素造成我們不得不去探索新方案。

          經過閑魚IM超復雜業務場景驗證Flutter模式的邏輯跨端可行性,閑魚在Flutter路上會一直保持前沿探索,最后能反饋到生態圈。

          總結一句話,探索過程在于你勇于邁出第一步,后面才會不斷驚喜發現。

          (原文鏈接:點此進入,本次有修訂和改動)

          附錄:更多文章匯總

          [1] 更多阿里團隊的文章分享:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

           

          [2] 更多IM開發綜合文章:

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

          移動端IM開發者必讀(一):通俗易懂,理解移動網絡的“弱”和“慢”

          移動端IM開發者必讀(二):史上最全移動弱網絡優化方法總結

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

          現代移動端網絡短連接的優化手段總結:請求速度、弱網適應、安全保障

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

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

          開發IM是自己設計協議用字節流好還是字符流好?

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

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

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

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

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

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

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

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

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

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

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

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

          開源IM工程“蘑菇街TeamTalk”的現狀:一場有始無終的開源秀

          如約而至:微信自用的移動端IM網絡層跨平臺組件庫Mars已正式開源

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

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

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

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

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

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

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

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

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

          IM“掃一掃”功能很好做?看看微信“掃一掃識物”的完整技術實現

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

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

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

          IM消息ID技術專題(四):深度解密美團的分布式ID生成算法

          IM消息ID技術專題(五):開源分布式ID生成器UidGenerator的技術實現

          IM消息ID技術專題(六):深度解密滴滴的高性能ID生成器(Tinyid)

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

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

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

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

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

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

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

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

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

          IM掃碼登錄技術專題(一):微信的掃碼登錄功能技術原理調試分析

          IM掃碼登錄技術專題(二):市面主流的掃碼登錄技術原理調試分析

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

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

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

          >> 更多同類文章 ……

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

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

          posted @ 2021-07-05 14:55 Jack Jiang 閱讀(200) | 評論 (0)編輯 收藏

               摘要: 本文作者張彥飛,原題“127.0.0.1 之本機網絡通信過程知多少 ”,首次發布于“開發內功修煉”,轉載請聯系作者。本次有改動。1、引言繼《你真的了解127.0.0.1和0.0.0.0的區別?》之后,這是我整理的第2篇有關本機網絡方面的網絡編程基礎文章。這次的文章由作者張彥飛原創分享,寫作本文的原因是現在本機網絡 IO 應用非常廣。在 php 中 一...  閱讀全文

          posted @ 2021-06-28 15:32 Jack Jiang 閱讀(235) | 評論 (0)編輯 收藏

               摘要: 本文由微信開發團隊工程師“virwu”分享。1、引言近期,微信小游戲支持了視頻號一鍵開播,將微信升級到最新版本,打開騰訊系小游戲(如跳一跳、歡樂斗地主等),在右上角菜單就可以看到發起直播的按鈕一鍵成為游戲主播了(如下圖所示)。然而微信小游戲出于性能和安全等一系列考慮,運行在一個獨立的進程中,在該環境中不會初始化視頻號直播相關的模塊。這就意味著小游戲的音視頻數據必須跨進程傳輸...  閱讀全文

          posted @ 2021-06-21 15:32 Jack Jiang 閱讀(173) | 評論 (0)編輯 收藏

          本文引用了“拍樂云Pano”的“深入淺出理解視頻編解碼技術”和“揭秘視頻千倍壓縮背后的技術原理之預測技術”文章部分內容,感謝原作者的分享。

          1、引言

          從 20 世紀 90 年代以來,數字音視頻編解碼技術迅速發展,一直是國內外研究的熱點領域。隨著5G的成熟和廣泛商用,帶寬已經越來越高,傳輸音視頻變得更加容易。視頻直播、視頻聊天,已經完全融入了每個人的生活。

          視頻為何如此普及呢?是因為通過視頻能方便快捷地獲取到大量信息。但視頻數據量非常巨大,視頻的網絡傳輸也面臨著巨大的挑戰。于是視頻編解碼技術就出場了。

          具體到實時視頻場景,不僅僅是數據量的問題,實時通信對時延要求、設備適配、帶寬適應的要求也非常高,要解決這些問題,始終離不開視頻編解碼技術的范疇。

          本文將從視頻編解碼技術的基礎知識入手,引出視頻編解碼技術中非常基礎且重要的預測技術,學習幀內預測和幀間預測的技術原理。

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

          2、相關文章

          如果你是音視頻技術初學者,以下3篇入門級干貨非常推薦一讀:

          零基礎,史上最通俗視頻編碼技術入門

          零基礎入門:實時音視頻技術基礎知識全面盤點

          實時音視頻面視必備:快速掌握11個視頻技術相關的基礎概念

          3、為什么需要視頻編解碼

          首先,來復習一下視頻編解碼方面的理論常識。

          視頻是由一系列圖片按照時間順序排列而成:

          • 1)每一張圖片為一幀;
          • 2)每一幀可以理解為一個二維矩陣;
          • 3)矩陣的每個元素為一個像素。

          一個像素通常由三個顏色進行表達,例如用RGB顏色空間表示時,每一個像素由三個顏色分量組成。每一個顏色分量用1個字節來表達,其取值范圍就是0~255。編碼中常用的YUV格式與之類似,這里不作展開。

          1280x720@60fps的視頻序列為例,十秒鐘的視頻有:1280*720*3*60*10 = 1.6GB

          如此大量的數據,無論是存儲還是傳輸,都面臨巨大的挑戰。視頻壓縮或者編碼的目的,也是為了保證視頻質量的前提下,將視頻減小,以利于傳輸和存儲。同時,為了能正確還原視頻,需要將其解碼。

          PS:限于篇幅,視頻編解碼方面的技術原理就不在此展開,有興趣強烈推薦從這篇深入學習:即時通訊音視頻開發(十九):零基礎,史上最通俗視頻編碼技術入門》。

          總之,視頻編解碼技術的主要作用就是:在可用的計算資源內,追求盡可能高的視頻重建質量和盡可能高的壓縮比,以達到帶寬和存儲容量的要求。

          為何突出“重建質量”?

          因為視頻編碼是個有損的過程,用戶只能從收到的視頻流中解析出“重建”畫面,它與原始的畫面已經不同,例如觀看低質量視頻時經常會碰到的“塊”效應。

          如何在一定的帶寬占用下,盡可能地保持視頻的質量,或者在保持質量情況下,盡可能地減少帶寬利用率,是視頻編碼的基本目標。

          用專業術語來說,即視頻編解碼標準的“率失真”性能:

          • 1)“率”是指碼率或者帶寬占用;
          • 2)“失真”是用來描述重建視頻的質量。

          與編碼相對應的是解碼或者解壓縮過程,是將接收到的或者已經存儲在介質上的壓縮碼流重建成視頻信號,然后在各種設備上進行顯示。

          4、什么是視頻編解碼標準

          視頻編解碼標準,通常只定義上述的解碼過程。

          例如 H.264 / AVC 標準,它定義了什么是符合標準的視頻流,對每一個比特的順序和意義都進行了嚴格地定義,對如何使用每個比特或者幾個比特表達的信息也有精確的定義。

          正是這樣的嚴格和精確,保證了不同廠商的視頻相關服務,可以很方便地兼容在一起,例如用 iPhone、Android Phone 或者 windows PC 都可以觀看同一在線視頻網站的同一視頻。

          世界上有多個組織進行視頻編碼標準的制定工作,國際標準組織 ISO 的 MPEG 小組、國際電信聯盟 ITU-T 的 VCEG 小組、中國的 AVS 工作組、Google 及各大廠商組成的開放媒體聯盟等。

          視頻編碼標準及發展歷史:

          自 VCEG 制定 H.120標準開始,視頻編碼技術不斷發展,先后成功地制定了一系列滿足不同應用場景的視頻編碼標準。VCEG 組織先后制定了H.120、H.261、H.262(MPEG-2 Part 2)、H.263、H.263+、H.263++

          MPEG也先后制定了MPEG-1、MPEG-2、MPEG-4 Part 2。以及兩個國際組織合作制定的H.264/AVC、H.265/HEVC、H.266/VVC

          中國自主知識產權的 AVS、AVS2、AVS3 視頻編碼標準;Google 制定的 VP8、VP9。

          Google、思科、微軟、蘋果等公司組成的開放媒體聯盟(AOM)制定的 AV1。

          這里特別提一下H.264/AVC:H.264/AVC雖有近20年歷史,但它優秀的壓縮性能、適當的運算復雜度、優秀的開源社區支持、友好的專利政策、強大的生態圈等多個方面的因素,依舊讓它保持著強大的生命力,特別是在實時通信領域。像 ZOOM、思科 Webex 等視頻會議產品和基于 WebRTC SDK 的視頻服務,大多數主流場景都采用 H.264/AVC。

          有關視頻編解碼標準,這里就不深入展開。更多詳細資料,可以讀一下下面這些精選文章:

          即時通訊音視頻開發(五):認識主流視頻編碼技術H.264

          即時通訊音視頻開發(十三):實時視頻編碼H.264的特點與優勢

          即時通訊音視頻開發(十七):視頻編碼H.264、VP8的前世今生

          愛奇藝技術分享:輕松詼諧,講解視頻編解碼技術的過去、現在和將來

          5、混和編碼框架

          縱觀視頻編解碼標準歷史,每一代視頻標準都在率失真性能上有著顯著的提升,他們都有一個核心的框架,就是基于塊的混合編碼框架(如下圖所示)。它是由J. R. Jain 和A. K. Jain在1979年的國際圖像編碼學會(PCS 1979)上提出了基于塊運動補償和變換編碼的混合編碼框架。

          我們一起來對該框架進行拆解和分析。

          從攝像頭采集到的一幀視頻:通常是 YUV 格式的原始數據,我們將它劃分成多個方形的像素塊依次進行處理(例如 H.264/AVC 中以16x16像素為基本單元),進行幀內/幀間預測、正變換、量化、反量化、反變換、環路濾波、熵編碼,最后得到視頻碼流。從視頻第一幀的第一個塊開始進行空間預測,因當前正在進行編碼處理的圖像塊和其周圍的圖像塊有相似性,我們可以用周圍的像素來預測當前的像素。我們將原始像素減去預測像素得到預測殘差,再將預測殘差進行變換、量化,得到變換系數,然后將其進行熵編碼后得到視頻碼流。

          接下來:為了可以使后續的圖像塊可以使用已經編碼過的塊進行預測,我們還要對變換系統進行反量化、反變換,得到重建殘差,再與預測值進行求合,得到重建圖像。最后我們對重建圖像進行環路濾波、去除塊效應等,這樣得到的重建圖像,就可以用來對后續圖像塊進行預測了。按照以上步驟,我們依次對后續圖像塊進行處理。

          對于視頻而言:視頻幀與幀的間隔大約只有十到幾十毫秒,通常拍攝的內容不會發生劇烈變化,它們之間存在非常強的相關性。

          如下圖所示,將視頻圖像分割成塊,在時間相鄰的圖像之間進行匹配,然后將匹配之后的殘差部分進行編碼,這樣可以較好地去除視頻信號中的視頻幀與幀之間的冗余,達到視頻壓縮的目的。這就是運動補償技術,直到今天它仍然是視頻編解碼的核心技術之一。

          運動估計和運動補償:

          變換編碼的核心思想:是把視頻數據分割成塊,利用正交變換將數據的能量集中到較少幾個變換系數上。結合量化和熵編碼,我們可以獲得更有效的壓縮。視頻編碼中信息的損失和壓縮比的獲得,很大程度上來源于量化模塊,就是將源信號中的單一樣本映射到某一固定值,形成多到少的映射,從而達到壓縮的目的,當然在壓縮的過程中就引入了損失。量化后的信號再進行無損的熵編碼,消除信號中的統計冗余。熵編碼的研究最早可以追溯到 20 世紀 50 年代,經過幾十年的發展,熵編碼在視頻編碼中的應用更加成熟、更加精巧,充分利用視頻數據中的上下文信息,將概率模型估計得更加準確,從而提高了熵編碼的效率。例如H.264/AVC中的Cavlc(基于上下文的變長編碼)、Cabac(基于上下文的二進制算術編碼)。算術編碼技術在后續的視頻編碼標準,如AV1、HEVC/H.265、VVC/H.266 中也有應用。

          視頻編碼發展至今,VVC/H.266 作為最新制定的標準,采納了一系列先進的技術,對混合編碼框架的各個部分都進行了優化和改進,使得其率失真性能相比前一代標準,又提高了一倍。

          例如:VVC/H.266 采用了128x128大小的基本編碼單元,并且可以繼續進行四叉樹劃分,支持對一個劃分進行二分、三分;色度分量獨立于亮度分量,支持單獨進行劃分;更多更精細的幀內預測方向、幀間預測模式;支持多種尺寸和形式的變換、環內濾波等。

          VVC/H.266 的制定,目標是對多種視頻內容有更好支持,例如屏幕共享內容、游戲、動漫、虛擬現實內容(VR、AR)等。其中也有特定的技術被采納進標準,例如調色板模式、幀內運動補償、仿射變換、跳過變換、自適應顏色變換等。   

          回到本文的正題,接下來的內容,我們著重介紹視頻編解碼中的預測技術。

          6、幀內預測技術

          視頻數據被劃分成方塊之后,相鄰的方塊的像素,以及方塊內的像素,顏色往往是逐漸變化的,他們之間有比較強的有相似性。這種相似性,就是空間冗余。既然存在冗余,就可以用更少的數據量來表達這樣的特征。

          比如:先傳輸第一個像素的值,再傳輸第二個像素相對于第一個像素的變化值,這個變化值往往取值范圍變小了許多,原來要8個bit來表達的像素值,可能只需要少于8個bit就足夠了。

          同樣的道理,以像素塊為基本單位,也可以進行類似的“差分”操作。我們從示例圖中,來更加直觀地感受一下這樣的相似性。

          如上圖中所標出的兩個8x8的塊:其亮度分量(Y)沿著“左上到右下”的方向,具有連續性,變化不大。

          假如:我們設計某種特定的“模式”,使其利用左邊的塊來“預測”右邊的塊,那么“原始像素”減去“預測像素”就可以減少傳輸所需要的數據量,同時將該“模式”寫入最終的碼流,解碼器便可以利用左側的塊來“重建”右側的塊。

          極端一點講:假如左側的塊的像素值經過一定的運算可以完全和右側的塊相同,那么編碼器只要用一個“模式”的代價,傳輸右側的塊。

          當然,視頻中的紋理多種多樣,單一的模式很難對所有的紋理都適用,因此標準中也設計了多種多樣的幀內預測模式,以充分利用像素間的相關性,達到壓縮的目的。

          例如下圖所示的H.264中9種幀內預測方向:以模式0(豎直預測)為例,上方塊的每個像素值(重建)各復制一列,得到幀內預測值。其它各種模式也采用類似的方法,不過,生成預測值的方式稍有不同。有這么多的模式,就產生了一個問題,對于一個塊而言,我們應該采用哪種模式來進行編碼呢?最佳的選擇方式,就是遍歷所有的模式進行嘗試,計算其編碼的所需的比特數和產生的質量損失,即率失真優化,這樣明顯非常復雜,因而也有很多種其它的方式來推斷哪種模式更好,例如基于SATD或者邊緣檢測等。

          從H.264的9種預測模式,到AV1的56種幀內方向預測模式,越來越多的模式也是為了更加精準地預測未編碼的塊,但是模式的增加,一方面增加了傳輸模式的碼率開銷,另一方面,從如此重多的模式中選一個最優的模式來編碼,使其能達到更高的壓縮比,這對編碼器的設計和實現也提出了更高的要求。

          7、幀間預測技術

          以下5張圖片是一段視頻的前5幀:可以看出,圖片中只有Mario和磚塊在運動,其余的場景大多是相似的,這種相似性就稱之為時間冗余。編碼的時候,我們先將第一幀圖片通過前文所述的幀內預測方式進行編碼傳輸,再將后續幀的Mario、磚塊的運動方向進行傳輸,解碼的時候,就可以將運動信息和第一幀一起來合成后續的幀,這樣就大大減少了傳輸所需的bit數。這種利用時間冗余來進行壓縮的技術,就是運動補償技術。該技術早在H.261標準中,就已經被采用。

          細心地讀者可能已經發現:Mario和磚塊這樣的物體怎么描述,才能讓它僅憑運動信息就能完整地呈現出來?

          其實視頻編碼中并不需要知道運動的物體的形狀,而是將整幀圖像劃分成像素塊,每個像素塊使用一個運動信息。即基于塊的運動補償。

          下圖中紅色圈出的白色箭頭即編碼磚塊和Mario時的運動信息,它們都指向了前一幀中所在的位置。Mario和磚塊都有兩個箭頭,說明它們都被劃分在了兩個塊中,每一個塊都有單獨的運動信息。這些運動信息就是運動矢量。運動矢量有水平和豎直兩個分量,代表是的一個塊相對于其參考幀的位置變化。參考幀就是已經編碼過的某一(多)個幀。

          當然:傳輸運動矢量本身就要占用很多 bit。為了提高運動矢量的傳輸效率,主要有以下措施。

          一方面:可以盡可能得將塊劃分變大,共用一個運動矢量,因為平坦區域或者較大的物體,他們的運動可能是比較一致的。從 H.264 開始,可變塊大小的運動補償技術被廣泛采用。

          另一方面:相鄰的塊之間的運動往往也有比較高的相似性,其運動矢量也有較高的相似性,運動矢量本身也可以根據相鄰的塊運動矢量來進行預測,即運動矢量預測技術;

          最后:運動矢量在表達物體運動的時候,有精度的取舍。像素是離散化的表達,現實中物體的運動顯然不是以像素為單位進行運動的,為了精確地表達物體的運動,需要選擇合適的精度來定義運動矢量。各視頻編解碼標準都定義了運動矢量的精度,運動矢量精度越高,越能精確地表達運動,但是代價就是傳輸運動矢量需要花費更多的bit。

          H.261中運動矢量是以整像素為精度的,H.264中運動矢量是以四分之一像素為精度的,AV1中還增加了八分之一精度。一般情況,時間上越近的幀,它們之間的相似性越高,也有例外,例如往復運動的場景等,可能相隔幾幀,甚至更遠的幀,會有更高的相似度。

          為了充分利用已經編碼過的幀來提高運動補償的準確度,從H.264開始引入了多參考幀技術。

          即:一個塊可以從已經編碼過的很多個參考幀中進行運動匹配,將匹配的幀索引和運動矢量信息都進行傳輸。

          那么如何得到一個塊的運動信息呢?最樸素的想法就是,將一個塊,在其參考幀中,逐個位置進行匹配檢查,匹配度最高的,就是最終的運動矢量。

          匹配度:常用的有SAD(Sum of Absolute Difference)、SSD(Sum of Squared Difference)等。逐個位置進行匹配度檢查,即常說的全搜索運動估計,其計算復雜度可想而知是非常高的。為了加快運動估計,我們可以減少搜索的位置數,類似的有很多算法,常用的如鉆石搜索、六邊形搜索、非對稱十字型多層次六邊形格點搜索算法等。

          以鉆石搜索為例,如下圖所示,以起始的藍色點為中心的9個匹配位置,分別計算這9個位置的SAD,如果SAD最小的是中心位置,下一步搜索中心點更近的周圍4個綠色點的SAD,選擇其中SAD最小的位置,繼續縮小范圍進行搜索;如果第一步中SAD最小的點不在中心,那么以該位置為中心,增加褐色的5或者3個點,繼續計算SAD,如此迭代,直到找到最佳匹配位置。

          編碼器在實現時,可根據實際的應用場景,對搜索算法進行選擇。

          例如:在實時音視頻場景下,計算復雜度是相對有限的,運動估計模塊要選擇計算量較小的算法,以平衡復雜度和編碼效率。當然,運動估計與運動補償的復雜度還與塊的大小,參考幀的個數,亞像素的計算等有關,在此不再深入展開。

          更多預測技術方面的原理這里就不再贅述。如果你對上面所述的預測技術理解上感到力不從心,這里有篇入門級的文章,可以先讀讀這篇《即時通訊音視頻開發(四):視頻編解碼之預測技術介紹》。

          8、寫在最后

          音視頻編解碼技術,歸根結底就是在有限的資源下(網絡帶寬、計算資源等),讓音質更清晰、視頻更高質。

          這其中,對于視頻來說,質量的提升仍然有很多可以深入研究的熱點問題。

          比如:基于人眼的主觀質量優化,主要利用人眼的視覺特性,將掩蔽效應、對比度靈敏度、注意力模型等與編碼相結合,合理分配碼率、減少編碼損失引起的視覺不適。

          AI在視頻編解碼領域的應用:包括將多種人工智能算法,如分類器、支持向量機、CNN等對編碼參數進行快速選擇,也可以使用深度學習對視頻進行編碼環外與編碼環內的處理,如視頻超分辨率、去噪、去霧、自適應動態范圍調整等編碼環外處理,達到提升視頻質量的目的。

          此外還有打破傳統混合編碼框架的深度神經網絡編碼,如Nvidia的Maxine視頻會議服務,利用深度學習來提取特征,然后對特征進行傳輸以節省帶寬。

          附錄:更多精華文章

          [1] 實時音視頻開發的其它精華資料:

          [2] 開源實時音視頻技術WebRTC的文章:

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

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

          posted @ 2021-06-15 11:25 Jack Jiang 閱讀(147) | 評論 (0)編輯 收藏

          本文作者“商文默”,有修訂和改動。

          1、寫在前面

          即時通訊網整理的大量IM技術文章中(見本文末“參考資料”一節),有關消息可靠性和一致性問題的文章占了很大比重,原因是IM這類系統拋開各種眼花繚亂的產品功能和技術特性,保證消息的可靠性和一致性幾乎是IM產品必需的素質。

          試想如果一個IM連發出的消息都不知道對方到底能不能收到、發出的聊天內容對方看到的到底是不是“胡言亂語”(嚴重亂序問題),這樣的APP用戶肯定不會讓他在手機上過夜(肯定第一時間卸載了),因為最基本的聊天邏輯都無法實現,它已經失去了IM軟件本身的意義。

          不過,另一個方面來講,IM系統是不標準的(雖然曾經XMPP這種協議試圖解決這個問題,但事實證明那根本不現實),各家幾乎都是自已的私有協議、不同的實現邏輯,這也決定了即使同一個技術問題,對于IM來說很難有固定的實現套路和標準的解決方案。

          所以,對于本文來說,文中作者雖然提供了有關IM消息“可靠性”與“一致性”問題的解決方案,但方案到底合不合理、適不適合你,這就是仁者見仁、智者見智的事了。用人話說就是:本文內容僅供參考,具體的解決方案請務結合自已的系統構架和實現情況,多閱讀幾篇即時通訊網上有關這個技術話題的文章,取其精華,找到適合自已的技術方案和思路才是最明智的。

          學習交流:

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

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

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

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

          2、本文引言

          叢所周之,即時通訊聊天(IM)系統必需要解決消息可靠性及消息一致性問題(PS:如果具體IM系統是什么你都還沒弄明白,先讀這篇《零基礎IM開發入門(一):什么是IM系統?)。

          這兩個問題,通俗來說就是:

          • 1)消息可靠性:簡單來說就是不丟消息,會話一方發送消息,消息成功到達對方并正確顯示;
          • 2)消息一致性:包括發送一方消息一致及會話雙方消息一致,要求消息不重復,不亂序。

          本文會從典型的IM消息發送邏輯開始,簡單易懂地闡明消息可靠性、一致性問題的原理及可參考的技術解決方法,或許技術方案并不完美,但希望能為你的IM技術問題解決帶來啟發。

          3、典型IM消息發送過程

          IM的消息發送一般的實現過程可以分為兩個階段:

          • 1)發送方發送消息、服務端接收、返回消息 ACK 給發送方;
          • 2)服務端將消息推送到接收方。

          判斷消息發送是否成功主要依據第一階段——即服務器是否接受到消息。

          對于消息發送者來說,消息狀態可以分為三類:

          • 1)正在發送;
          • 2)發送成功;
          • 3)發送失敗。

          具體來說,這三類狀態的具體意義是:

          • 1)正在發送:發送方觸發發送事件開始,到收到服務端返回消息對應 ACK 之前;
          • 2)發送成功:發送方收到消息對應 ACK 回復;
          • 3)發送失敗:超過一定重發次數,未收到消息對應 ACK 回復。

          對應的消息發送流程如下圖所示:

          4、IM消息可靠性

          限于篇幅,對于IM消息可靠性的基本概念和詳細原理建議閱讀《零基礎IM開發入門(三):什么是IM系統的可靠性?》,本文著重談談解決思路。

          4.1 重發機制

          保證消息發送第一階段(見本文“3、典型IM消息發送過程”一節)消息成功發送的方法是設立重發機制:

          • 1)依據一定時長內是否收到消息對應 ACK,判斷消息是否要重發;
          • 2)如果超過預設時長,就重新發送;
          • 3)當重發次數超過預設次數,就不再重發,判定該消息發送失敗,修改消息發送狀態。

          PS:具體的完整方案級代碼實現,可以參考MobileIMSDK  中有關QoS機制的代碼實現。

          4.2 會話記錄檢查

          消息發送第二階段(見本文“3、典型IM消息發送過程”一節)服務端推送消息到接收方,如果連接斷開,會丟失消息。

          所以要保證消息完整,就需要在建立連接后,根據上一條消息(已經 ACK)時間戳,獲取會話記錄,一次返回一段時間內所有消息(PS:中大型應用中,消息的拉取也不是個簡單事情,詳情可以閱讀《IM開發干貨分享:如何優雅的實現大量離線消息的可靠投遞)。

          另一種保證方法是加入定時輪詢,檢查消息完整性,具體的思路如下圖所示。

          建立連接流程圖:

          4.3 需要考慮的兩個問題

          消息重發、會話記錄檢查需要考慮兩個問題:

          • 1)消息是否會重復發送;
          • 2)消息順序是否會被打亂。

          舉兩個例子。

          關于消息重發問題:

          • 1)如果丟消息的點在消息達到服務端之前,服務端并沒有收到消息,發送方重新發送丟失消息,服務端接收成功,不會產生兩條相同消息;
          • 2)而如果服務端接收到消息,返回 ACK 丟失,這時再發送一次相同消息,就可能造成消息重復。

          關于消息順序問題:

          • 1)如果發送方連發三條消息,第一、第三條成功被服務端接收,第二條丟了,那第三條消息是否會被記錄?
          • 2)如果這時第二條消息達到服務端,其順序是在第三條時間之前還是之后(服務端一般都會給記錄打一個時間戳)?

          5、IM消息一致性

          同上節一樣,對于IM消息一致性的基本概念和詳細原理建議閱讀《零基礎IM開發入門(四):什么是IM系統的消息時序一致性?》。

          5.1 使用 uuid 消息去重

          對于消息重發問題,可以給每條消息增加屬性 uuid 作為消息唯一標識,重發消息 uuid 不變,前端根據 uuid 去重。大致思路就是這樣。

          PS:對于IM來說,消息ID也是個很大的技術話題,有興趣可以讀下面這個系列:

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

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

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

          IM消息ID技術專題(四):深度解密美團的分布式ID生成算法

          IM消息ID技術專題(五):開源分布式ID生成器UidGenerator的技術實現

          IM消息ID技術專題(六):深度解密滴滴的高性能ID生成器(Tinyid)

          5.2 使用向量時鐘進行消息排序

          對于消息排序問題:因為在聊天中,消息的順序對于發送方的表述有重要的影響,消息不完整或順序顛倒都可能造成語意不連貫,甚至曲解。所以需要保證發送方發送消息順序,而會話雙方消息排序需要考慮實際情況。

          在一般的認知里:狀態是正在發送的消息,應該還沒有被對方看到,只有發送成功的消息,才會被對方看到。但在實現中,消息發送成功是以服務器接收消息并返回 ACK 成功為判斷依據,而不是被對方接收到。

          那么就會出現這樣一個問題:如果一條消息狀態是正在發送,此時收到一條消息,那么收到的消息是在正在發送的消息之前還是之后?

          這是一個上下文關系,關鍵問題是:發送方是以哪條所見消息為依據發送消息的。

          這里提供一種思路:借鑒分布式系統中的向量時鐘算法(見《分布式系統中的向量時鐘算法)。

          先簡單描述向量時鐘算法:

          向量時鐘算法用于在分布式系統中生成事件偏序關系,并糾正因果關系。一個系統包含 N 個節點,每個節點產生的消息體中包含該節點的邏輯時鐘,整體系統的向量時鐘由 N 維邏輯時鐘組成,并在每個節點產生的消息體中傳遞。

          簡單來說,向量時鐘算法的實現原理如下:

          • 1)初始狀態,向量值為 0;
          • 2)每次節點處理完節點事件,該節點時鐘+1;
          • 3)每次節點發送消息,將包含自身時鐘的系統向量時鐘一起發送;
          • 4)每次節點收到消息,更新向量時鐘,該節點時鐘+1,其他節點對比每個節點本地保留的向量時鐘值和消息體中向量時鐘值,取最大值;
          • 5)節點同時收到多條消息,判斷接收消息的向量時鐘之間是否存在偏序關系。

          針對上述的第5)點:

          • 1)如果存在偏序關系,則合并向量時鐘,取偏序較大的向量時鐘;
          • 2)如果不存在偏序關系,則不能合并。

          偏序關系:如果 A 向量中的每一維都大于等于 B 向量,則 A、B 之間存在偏序關系,否則不存在偏序關系。

          對于IM為聊天消息排序來說,其實就是處理聊天消息的上下文語境,決定消息之間的因果關系。

          參考向量時鐘算法:假設有 N 個消息會話方,系統的向量時鐘由 N 維時鐘組成,向量時鐘在各方發送的消息體中傳遞,并依據向量時鐘排序。

          具體實現思路如下:

          • 1)系統向量時鐘設為 (0, 0, …, N);
          • 2)節點發送消息,更新系統向量時鐘,該節點時鐘加一,其他節點不變;
          • 3)節點接收消息,更新系統向量時鐘,該節點時鐘加一;其他節點對比每個節點本地保留的向量時鐘的值和消息中向量時鐘的值,取最大值;
          • 4)依據消息體內系統向量時鐘的偏序關系決定消息順序。

          針對上述第4)點:

          • 1)如果可以確定偏序關系,則根據偏序關系由小到大顯示;
          • 2)如果多條消息不能確定偏序關系,則按照自然順序(接收到的順序)顯示。

          向量時鐘在理論上可以解決大部分消息一致性的問題,但在實現中還需要考慮實際使用時的體驗。

          這其中最需要關注的問題是:是否要強制排序,或者說,如果實際顯示順序和向量時鐘之間的偏序關系不一致,是否要移動消息之間的順序。

          舉個例子:在一個有多人的會話中,如果有一方網速特別慢,收不到消息,也發不出消息。在他看到的最后的消息之后,其他人已經開始新的話題,這時他關于上一個話題的消息終于發送成功,并被其他人收到。

          此時就存在這樣一個問題:這條關于上一個話題的消息是顯示在最后,還是移到較早時間?

          • 1)如果顯示在最后,但消息內容和目前的話題不相關,其他人可能會感到莫名其妙;
          • 2)如果把消息移到較早時間,那么這條消息可能不會被其他人看到,或者看到前面多了一條消息,會有種突兀的感覺。

          IM 的場景很多,也很復雜,更多的時候需要從產品角度考慮問題。

          對于消息是否需要排序的問題,這里只提出一個比較通用的方案:建議會話中不強制排序,會話歷史記錄中按照向量時鐘的偏序關系進行排序。

          6、本文小結

          對于 IM 系統消息可靠性及一致性問題,通過消息重發機制保證消息成功被服務端接收,通過會話記錄檢查保證收取消息完整,從而保證整個消息發送過程的可靠性。使用 uuid 消息去重,參考向量時鐘算法進行消息排序,為保證消息一致性提供一種解決方案。

          總之,IM這類系統看似簡單,實則水深似海,如果你是IM開發新手,可以從《新手入門一篇就夠:從零開發移動端IM》這篇入手系統學習。如果你自認為已是IM老手,這里整理的 IM中大型架構設計 方面的文章或許可以參考一下。

          7、參考資料

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

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

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

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

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

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

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

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

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

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

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

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

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

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

          posted @ 2021-06-07 20:34 Jack Jiang 閱讀(178) | 評論 (0)編輯 收藏

               摘要: 本文由喜馬拉雅技術團隊原創分享,原題《喜馬拉雅自研網關架構實踐》,有改動。1、引言網關是一個比較成熟的產品,基本上各大互聯網公司都會有網關這個中間件,來解決一些公有業務的上浮,而且能快速的更新迭代。如果沒有網關,要更新一個公有特性,就要推動所有業務方都更新和發布,那是效率極低的事,有網關后,這一切都變得不是問題。喜馬拉雅也是一樣,用戶數增長達到 6 億多的級別,Web 服務個數達到500+,目前我...  閱讀全文

          posted @ 2021-05-31 10:30 Jack Jiang 閱讀(244) | 評論 (0)編輯 收藏

               摘要: 本文來自“糊糊糊糊糊了”的分享,原題《實時消息推送整理》,有優化和改動。1、寫在前面對Web端即時通訊技術熟悉的開發者來說,我們回顧網頁端IM的底層通信技術,從短輪詢、長輪詢,到后來的SSE以及WebSocket,使用門檻越來越低(早期的長輪詢Comet這類技術實際屬于hack手段,使用門檻并不低),技術手段越來越先進,網頁端即時通訊技術的體驗也因此越來越好。但上周在編輯《...  閱讀全文

          posted @ 2021-05-25 12:18 Jack Jiang 閱讀(181) | 評論 (0)編輯 收藏

          本文由愛奇藝技術團隊原創分享,原題《構建通用WebSocket推送網關的設計與實踐》,有優化和改動。

          1、引言

          叢所周之,HTTP協議是一種無狀態、基于TCP的請求/響應模式的協議,即請求只能由客戶端發起、由服務端進行響應。在大多數場景,這種請求/響應的Pull模式可以滿足需求。但在某些情形:例如消息推送(IM中最為常見,比如IM的離線消息推送)、實時通知等應用場景,需要實時將數據同步到客戶端,這就要求服務端支持主動Push數據的能力。

          傳統的Web服務端推送技術歷史悠久,經歷了短輪詢、長輪詢等階段的發展(見《新手入門貼:史上最全Web端即時通訊技術原理詳解),一定程度上能夠解決問題,但也存在著不足,例如時效性、資源浪費等。HTML5標準帶來的WebSocket規范基本結束了這一局面,成為目前服務端消息推送技術的主流方案。

          在系統中集成WebSocket十分簡單,相關討論與資料很豐富。但如何實現一個通用的WebSocket推送網關尚未有成熟的方案。目前的云服務廠商主要關注iOS和安卓等移動端推送,也缺少對WebSocket的支持。本文分享了愛奇藝基于Netty實現WebSocket長連接實時推送網關時的實踐經驗總結。

          學習交流:

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

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

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

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

          2、專題目錄

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

          長連接網關技術專題(一):京東京麥的生產級TCP網關技術實踐總結

          長連接網關技術專題(二):知乎千萬級并發的高性能長連接網關技術實踐

          長連接網關技術專題(三):手淘億級移動端接入層網關的技術演進之路

          長連接網關技術專題(四):愛奇藝WebSocket實時推送網關技術實踐》(* 本文

          其它相關技術文章:

          絕對干貨:基于Netty實現海量接入的推送服務技術要點

          京東到家基于Netty的WebSocket應用實踐分享

          愛奇藝技術團隊分享的其它文章:

          愛奇藝技術分享:輕松詼諧,講解視頻編解碼技術的過去、現在和將來

          愛奇藝技術分享:愛奇藝Android客戶端啟動速度優化實踐總結

          愛奇藝移動端網絡優化實踐分享:網絡請求成功率優化篇

          3、舊方案存在的技術痛點

          愛奇藝號是我們內容生態的重要組成,作為前臺系統,對用戶體驗有較高要求,直接影響著創作者的創作熱情。

          目前,愛奇藝號多個業務場景中用到了WebSocket實時推送技術,包括:

          • 1)用戶評論:實時的將評論消息推送到瀏覽器;
          • 2)實名認證:合同簽署前需要對用戶進行實名認證,用戶掃描二維碼后進入第三方的認證頁面,認證完成后異步通知瀏覽器認證的狀態;
          • 3)活體識別:類似實名認證,當活體識別完成后,異步將結果通知瀏覽器。

          在實際的業務開發中,我們發現,WebSocket實時推送技術在使用中存在一些問題。

          這些問題是:

          • 1)首先:WebSocket技術棧不統一,既有基于Netty實現的,也有基于Web容器實現的,給開發和維護帶來困難;
          • 2)其次:WebSocket實現分散在在各個工程中,與業務系統強耦合,如果有其他業務需要集成WebSocket,面臨著重復開發的窘境,浪費成本、效率低下;
          • 3)第三:WebSocket是有狀態協議的,客戶端連接服務器時只和集群中一個節點連接,數據傳輸過程中也只與這一節點通信。WebSocket集群需要解決會話共享的問題。如果只采用單節點部署,雖然可以避免這一問題,但無法水平擴展支撐更高負載,有單點的風險;
          • 4)最后:缺乏監控與報警,雖然可以通過Linux的Socket連接數大致評估WebSocket長連接數,但數字并不準確,也無法得知用戶數等具有業務含義的指標數據;無法與現有的微服務監控整合,實現統一監控和報警。

          PS:限于篇幅本文不詳細介紹WebSocket技術本身,有興趣可以詳讀《WebSocket從入門到精通,半小時就夠!》。

          4、新方案的技術目標

          如上節所示,為了解決舊方案中存在的問題,我們需要實現統一的WebSocket長連接實時推送網關。

          這套新的網關需要具備如下特點:

          • 1)集中實現長連接管理和推送能力:統一技術棧,將長連接作為基礎能力沉淀,便于功能迭代和升級維護;
          • 2)與業務解耦:將業務邏輯與長連接通信分離,使業務系統不再關心通信細節,也避免了重復開發,浪費研發成本;
          • 3)使用簡單:提供HTTP推送通道,方便各種開發語言的接入。業務系統只需要簡單的調用,就可以實現數據推送,提升研發效率;
          • 4)分布式架構:實現多節點的集群,支持水平擴展應對業務增長帶來的挑戰;節點宕機不影響服務整體可用性,保證高可靠;
          • 5)多端消息同步:允許用戶使用多個瀏覽器或標簽頁同時登陸在線,保證消息同步發送;
          • 6)多維度監控與報警:自定義監控指標與現有微服務監控系統打通,出現問題時可及時報警,保證服務的穩定性。

          5、新方案的技術選型

          在眾多的WebSocket實現中,從性能、擴展性、社區支持等方面考慮,最終選擇了Netty。Netty是一個高性能、事件驅動、異步非阻塞的網絡通信框架,在許多知名的開源軟件中被廣泛使用。

          PS:如果你對Netty知之甚少,可以詳讀以下兩篇:

          WebSocket是有狀態的,無法像直接HTTP以集群方式實現負載均衡,長連接建立后即與服務端某個節點保持著會話,因此集群下想要得知會話屬于哪個節點有點困難。

          解決以上問題一般有兩種技術方案:

          • 1)一種是使用類似微服務的注冊中心來維護全局的會話映射關系;
          • 2)一種是使用事件廣播由各節點自行判斷是否持有會話,兩種方案對比如下表所示。

          WebSocket集群方案:

          綜合考慮實現成本與集群規模,選擇了輕量級的事件廣播方案。

          實現廣播可以選擇基于RocketMQ的消息廣播、基于Redis的Publish/Subscribe、基于ZooKeeper的通知等方案,其優缺點對比如下表所示。從吞吐量、實時性、持久化、實現難易等方面考慮,最終選擇了RocketMQ。

          廣播的實現方案對比:

          6、新方案的實現思路

          6.1 系統架構

          網關的整體架構如下圖所示:

          網關的整體流程如下:

          1)客戶端與網關任一節點握手建立起長連接,節點將其加入到內存維護的長連接隊列。客戶端定時向服務端發送心跳消息,如果超過設定的時間仍沒有收到心跳,則認為客戶端與服務端的長連接已斷開,服務端會關閉連接,清理內存中的會話。

          2)當業務系統需要向客戶端推送數據時,通過網關提供的HTTP接口將數據發向網關。

          3)網關在接收到推送請求后,將消息寫入RocketMQ。

          4)網關作為消費者,以廣播模式消費消息,所有節點都會接收到消息。

          5)節點接收到消息后判斷推送的消息目標是否在自己內存中維護的長連接隊列里,如果存在則通過長連接推送數據,否則直接忽略。

          網關以多節點方式構成集群,每節點負責一部分長連接,可實現負載均衡,當面對海量連接時,也可以通過增加節點的方式分擔壓力,實現水平擴展。

          同時,當節點出現宕機時,客戶端會嘗試重新與其他節點握手建立長連接,保證服務整體的可用性。

          6.2 會話管理

          WebSocket長連接建立起來后,會話維護在各節點的內存中。SessionManager組件負責管理會話,內部使用了哈希表維護了UID與UserSession的關系。

          UserSession代表用戶維度的會話,一個用戶可能會同時建立多個長連接,因此UserSession內部同樣使用了一個哈希表維護Channel與ChannelSession的關系。

          為了避免用戶無限制的創建長連接,UserSession在內部的ChannelSession超過一定數量后,會將最早建立的ChannelSession關閉,減少服務器資源占用。SessionManager、UserSession、ChannelSession的關系如下圖所示。

          SessionManager組件:

          6.3 監控與報警

          為了了解集群建立了多少長連接、包含了多少用戶,網關提供了基本的監控與報警能力。

          網關接入了Micrometer,將連接數與用戶數作為自定義指標暴露,供Prometheus進行采集,實現了與現有的微服務監控系統打通。

          Grafana中方便地查看連接數、用戶數、JVM、CPU、內存等指標數據,了解網關當前的服務能力與壓力。報警規則也可以在Grafana中配置,當數據異常時觸發奇信(內部報警平臺)報警。

          7、新方案的性能壓測

          壓測準備:

          • 1)壓測選擇兩臺配置為4核16G的虛擬機,分別作為服務器和客戶端;
          • 2)壓測時選擇為網關開放了20個端口,同時建立20個客戶端;
          • 3)每個客戶端使用一個服務端端口建立起5萬連接,可以同時創建百萬個連接。

          連接數(百萬級)與內存使用情況如下圖所示:

          給百萬個長連接同時發送一條消息,采用單線程發送,服務器發送完成的平均耗時在10s左右,如下圖所示。

          服務器推送耗時: 

          一般同一用戶同時建立的長連接都在個位數。以10個長連接為例,在并發數600、持續時間120s條件下壓測,推送接口的TPS大約在1600+,如下圖所示。

          長連接10、并發600、持續時間120s的壓測數據:

          當前的性能指標已滿足我們的實際業務場景,可支持未來的業務增長。

          8、新方案的實際應用案例

          為了更生動的說明優化效果,文章最后,我們也以封面圖添加濾鏡效果為例,介紹一個愛奇藝號使用新WebSocket網關方案的案例。

          愛奇藝號自媒體發表視頻時,可選擇為封面圖添加濾鏡效果,引導用戶提供提供更優質的封面。

          當用戶選擇一個封面圖后,會提交異步的后臺處理任務。當異步任務處理完成后,通過WebSocket將不同濾鏡效果處理后的圖片返回給瀏覽器,業務場景如下圖所示。

          從研發效率方面考慮,如果在業務系統中集成WebSocket,至少需要1-2天的開發時間。

          如果直接使用新的WebSocket網關的推送能力,只需要簡單的接口調用就實現了數據推送,開發時間降低到分鐘級別,研發效率大大提高。

          從運維成本方面考慮,業務系統不再含有與業務邏輯無關的通信細節,代碼的可維護性更強,系統架構變得更加簡單,運維成本大大降低。

          9、寫在最后

          WebSocket是目前實現服務端推送的主流技術,恰當使用能夠有效提供系統響應能力,提升用戶體驗。通過WebSocket長連接網關可以快速為系統增加數據推送能力,有效減少運維成本,提高開發效率。

          長連接網關的價值在于:

          • 1)它封裝了WebSocket通信細節,與業務系統解耦,使得長連接網關與業務系統可獨立優化迭代,避免重復開發,便于開發與維護;
          • 2)網關提供了簡單易用的HTTP推送通道,支持多種開發語言接入,便于系統集成和使用;
          • 3)網關采用了分布式架構,可以實現服務的水平擴容、負載均衡與高可用;
          • 4)網關集成了監控與報警,當系統異常時能及時預警,保證服務的健康和穩定。

          目前,新的WebSocket長連接實時網關已在愛奇藝號圖片濾鏡結果通知、MCN電子簽章等多個業務場景中得到應用。

          未來還有許多方面需要探索,例如消息的重發與ACK、WebSocket二進制數據的支持、多租戶的支持等。

          附錄:更多相關技術資料

          [1] 有關WEB端即時通訊開發:

          新手入門貼:史上最全Web端即時通訊技術原理詳解

          Web端即時通訊技術盤點:短輪詢、Comet、Websocket、SSE

          SSE技術詳解:一種全新的HTML5服務器推送事件技術

          Comet技術詳解:基于HTTP長連接的Web端實時通信技術

          新手快速入門:WebSocket簡明教程

          WebSocket詳解(一):初步認識WebSocket技術

          WebSocket詳解(二):技術原理、代碼演示和應用案例

          WebSocket詳解(三):深入WebSocket通信協議細節

          WebSocket詳解(四):刨根問底HTTP與WebSocket的關系(上篇)

          WebSocket詳解(五):刨根問底HTTP與WebSocket的關系(下篇)

          WebSocket詳解(六):刨根問底WebSocket與Socket的關系

          socket.io實現消息推送的一點實踐及思路

          LinkedIn的Web端即時通訊實踐:實現單機幾十萬條長連接

          Web端即時通訊技術的發展與WebSocket、Socket.io的技術實踐

          Web端即時通訊安全:跨站點WebSocket劫持漏洞詳解(含示例代碼)

          開源框架Pomelo實踐:搭建Web端高性能分布式IM聊天服務器

          使用WebSocket和SSE技術實現Web端消息推送

          詳解Web端通信方式的演進:從Ajax、JSONP 到 SSE、Websocket

          MobileIMSDK-Web的網絡層框架為何使用的是Socket.io而不是Netty?

          理論聯系實際:從零理解WebSocket的通信原理、協議格式、安全性

          微信小程序中如何使用WebSocket實現長連接(含完整源碼)

          八問WebSocket協議:為你快速解答WebSocket熱門疑問

          Web端即時通訊實踐干貨:如何讓你的WebSocket斷網重連更快速?

          WebSocket從入門到精通,半小時就夠!

          WebSocket硬核入門:200行代碼,教你徒手擼一個WebSocket服務器

          >> 更多同類文章 ……

          [2] 有關推送技術的文章:

          一個基于MQTT通信協議的完整Android推送Demo

          求教android消息推送:GCM、XMPP、MQTT三種方案的優劣

          移動端實時消息推送技術淺析

          絕對干貨:基于Netty實現海量接入的推送服務技術要點

          極光推送系統大規模高并發架構的技術實踐分享

          魅族2500萬長連接的實時消息推送架構的技術實踐分享

          專訪魅族架構師:海量長連接的實時消息推送系統的心得體會

          基于WebSocket實現Hybrid移動應用的消息推送實踐(含代碼示例)

          一個基于長連接的安全可擴展的訂閱/推送服務實現思路

          實踐分享:如何構建一套高可用的移動端消息推送系統?

          Go語言構建千萬級在線的高并發消息推送系統實踐(來自360公司)

          騰訊信鴿技術分享:百億級實時消息推送的實戰經驗

          百萬在線的美拍直播彈幕系統的實時推送技術實踐之路

          京東京麥商家開放平臺的消息推送架構演進之路

          技術干貨:從零開始,教你設計一個百萬級的消息推送系統

          長連接網關技術專題(四):愛奇藝WebSocket實時推送網關技術實踐

          >> 更多同類文章 ……

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

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

          posted @ 2021-05-17 23:12 Jack Jiang 閱讀(300) | 評論 (0)編輯 收藏

               摘要: 本文引用了作者“大古同學”的“二維碼掃碼登錄是什么原理”一文的主要內容,為了更好的理解和閱讀,即時通訊網收錄時有修訂和改動,感謝原作者的分享。1、引言自從微信的PC端使用掃碼登陸認證邏輯后,這種方式似乎在越來越多的IM中看到(雖然我個人認為這種登錄方式很酷,但并不方便,尤其手機不大身邊的時候)。 ▲ 上圖微信PC端的掃碼登錄界面...  閱讀全文

          posted @ 2021-05-10 13:42 Jack Jiang 閱讀(193) | 評論 (0)編輯 收藏

          僅列出標題
          共50頁: First 上一頁 23 24 25 26 27 28 29 30 31 下一頁 Last 
          Jack Jiang的 Mail: jb2011@163.com, 聯系QQ: 413980957, 微信: hellojackjiang
          主站蜘蛛池模板: 竹溪县| 肇庆市| 三都| 哈巴河县| 钟祥市| 正阳县| 谷城县| 保定市| 溆浦县| 白朗县| 茌平县| 富阳市| 台前县| 左贡县| 阿克陶县| 永修县| 丹巴县| 尼勒克县| 道真| 洛浦县| 通渭县| 吴旗县| 平远县| 建水县| 文昌市| 黄梅县| 浦江县| 静乐县| 嘉义市| 略阳县| 婺源县| 巴林左旗| 五指山市| 东港市| 会同县| 萨嘎县| 台中市| 蒙城县| 邵武市| 平原县| 深圳市|