Jack Jiang

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

          本文由vivo互聯網服務器團隊李青鑫分享,有較多修訂和改動。

          1、引言

          本文內容來自vivo互聯網服務器團隊李青鑫在“2021 vivo開發者大會”現場的演講內容整理而成(現場演講稿可從本文末附件中下載)。

          本文將要分享的是手機廠商vivo的系統級推送平臺在架構設計上的技術實踐和總結。這也是目前為止首次由手機廠商分享的自建系統級推送平臺的技術細節,我們也得以借此機會一窺廠商ROOM級推送通道的技術水準。

          學習交流:

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

          2、關于作者

          李青鑫,vivo互聯網服務器團隊架構師。

          3、為什么需要消息推送

          消息推送對于移動端APP來說,是很常見的業務特征,比如新聞APP中的最新資訊、社交應用中的系統通知、IM即時通訊應用的離線聊天消息等等。

          可以說,沒有消息推送能力,APP就失去了實時觸達的能力,對于一個應用來說,它對用戶的“粘性”將大大下降。而對于用戶來說,信息實時獲取的能力也將大大降低,用戶體驗也將大幅下降。

          4、消息推送的技術障礙

          以我們日常最常見的IM應用來說,離線消息的推送是必備能力。

          但隨著Android系統的不斷升級,離線推送已經不單單是一個后臺服務加長連接那么理所當然了。

          對于早期的Android系統來說,想要實現IM的離線消息推送并不困難,搞個后臺服務再加上socket長連接就算是齊活了。

          但隨著Android系統的升級,針對后臺進程和網絡服務限制不斷加碼,為了繼續實現離線消息的推送,開發者們不得不跟系統斗志斗勇,搞出了各種保活黑科技,比如:Android4.0之后的雙進程守護、Android6.0及之后的防殺復活術、以及發展到后來的騰訊TIM進程永生技術,一時間群魔亂舞、無比風騷(有興趣的同學,可以讀讀《Android進程永生技術終極揭秘:進程被殺底層原理、APP應對被殺技巧》這篇針對所有保活黑科技的總結性文章)。

          隨著Andriod 9.0的到來,基本從系統上堵死了各種保活黑科技的活路(詳見《Android P正式版即將到來:后臺應用保活、消息推送的真正噩夢),各Android廠商的ROOM系統級推送通道也應運而生——華為推送小米推送魅族推送OPPO推送vivo推送,一時間從用戶的噩夢(保活黑科技對用戶困擾很大)變成了開發者的惡夢并持續至今(想要做好IM離線推送,如今的IM開發者們不得不一家家對接各手機廠商的離線推送,你說煩不煩)。

          也別跟我說為什么不用Android官方的FCM服務在國內這鏈接你能打開算我輸,至于為什么,你懂的。。。),也別我跟提那個統一推送聯盟4、5年過去了,看樣子還要繼續等下去)。

          于是,為了繼續搞定離線消息推送,IM的開發者們目前只有兩條路可選:

          隨著Android系統對于開發者保活黑科技的“堵”,手機廠商們搞出自家的系統級推送通道來“疏”,也算是理所當然。而這些廠商之中,vivo的系統級推送通道出現的算是比較晚的。

          本篇文章的余下技術內容,算是目前為止首次由手機廠商分享的自建系統級推送平臺的技術細節,我們一起來學習。

          5、從技術角度了解推送平臺

          推送平臺是做什么的?

          從技術的角度上來看,推送平臺就是一個通過TCP長連接,將消息發送給用戶的平臺。所以推送平臺的本質其實就是借助網絡通道,將消息發送到用戶設備上。

          大家日常都收到過快遞通知吧!當快遞員將快遞放到快遞柜中,快遞后臺就會自動推送一條消息,通知你有快遞。我相信,如果你是一位運營人員,你也會喜歡這種自動下發消息高效的方式。

          大家感興趣的,可以通過vivo開放平臺入口,選擇消息推送來更進一步了解更多技術細節,這里就不做展開了。

          6、短連接與長連接

          消息推送平臺的本質,就是通過長連接將內容、服務、用戶連在一起,將內容分發給用戶,為終端設備提供實時、雙向通信能力。

          這里有個概念長連接,那么什么是長連接?

          所謂的長連接就是:客戶端與服務端維持的一條、在相對較長的時間里、都能夠進行網絡通信的網絡連接(比如:基于TCP的長連接)。

          為什么我們要采用長連接而不是短連接作為平臺底層的網絡通信?

          先來看看短連接下消息下發的場景:使用短連接的方式就是輪詢,即客戶端定時的去詢問后臺有沒有設備A的消息,當有設備A的消息時后臺返回對應的消息,可能很多情況下都是無功而返,浪費流量。當后臺有消息需要發送給設備A時,因為設備A沒有過來取導致消息無法下發。

          而使用長連接:當有設備A的消息時后臺直接發送給設備A而不用等設備A自己過拉取,所以長連接讓數據交互更加自然、高效。

          7、業務需求驅動架構升級

          對于系統的技術架構來說,它是動態的,不同階段都可能會發生變化。而推動架構進行演進的推力,主要來自于業務需求,一起來回顧,平臺的業務發展歷程。

          自2015年立項以來,隨著業務量增長,不斷為系統添加功能特性,豐富整個系統的能力使其滿足不同的業務場景需求。比如支持內容完全審核、支持IM、支持IoT、支持WebSocket 通信等。

          從圖上可以看到,業務量幾乎每年都有幾十億的增長,不斷攀高,給系統帶來了挑戰,原有的系統架構存在的問題,也逐漸浮出水面,比如延遲、性能瓶頸。

          架構服務于業務,2018年之前我們平臺所有服務都放在云上,但是依賴的其他內部業務部署在自建機房。

          隨著業務量增長與自建機房的數據傳輸,已經出現了延遲的問題,并且在逐漸惡化,不利于我們平臺功能的拓展。

          所以在2018年下半年,我們對部署架構進行調整:將所有核心邏輯模塊都遷移到自建機房,架構優化之后,數據延遲問題得到徹底解決,同時也為架構進一步演進奠定了基礎。從上面的圖中可以看到我們接入網關也進行優化三地部署。

          為什么要進行三地部署而不是更多區域部署呢?

          主要基于以下三點考慮:

          • 1)第一是基于用戶分布及成本的考慮;
          • 2)第二是能為用戶提供就近接入;
          • 3)第三是能夠讓接入網關具備一定容災能力。

          大家可以設想下,如果沒有三地部署,接入網關機房故障時,那么平臺就癱瘓了。

          隨著平臺業務規模的進一步擴大,日吞吐量達到10億的量級,用戶對于時效性、并發要求越來越高。而2018年的邏輯服務的系統架構已經無法業務高并發的需求或者需要更高的服務器成本才能滿足高并發需求。

          所以從平臺功能、成本優化出發,在2019年對系統進行了重構,為用戶提供更加豐富的產品功能及更穩定、更高性能的平臺。

          8、利用長連接能力給更多業務賦能

          作為公司較大規模的長連接服務平臺,團隊積累了非常豐富的長連接經驗。我們也一直在思考,如何讓長連接能力為更多業務賦能。

          我們平臺服務端各個模塊之間通過RPC調用,這是一種非常高效的開發模式,不用每個開發人員都去關心底層網絡層數據包的。

          我們設想一下,如果客戶端也能通過RPC調用后臺,這一定是非常棒的開發體驗。

          未來我們將會提供VRPC通信框架,用于解決客戶端與后臺通信及開發效率問題,為客戶端與后臺提供一致的開發體驗,讓更多的開發人員不再關心網絡通信問題,專心開發業務邏輯。

          作為一個吞吐量超過百億的推送平臺其穩定性、高性能、安全都非常重要,接下來和大家分享,我們在系統穩定性、高性能、安全方面的實踐經驗。

          9、vivo推送平臺的領域模型

          從上圖的領域模型可以看出,推送平臺以通信服務作為核心能力,在核心能力的基礎上我們又提供了,大數據服務以及運營系統,通過不同接口對外提供不同的功能、服務。

          以通信服務為核心的推送平臺,其穩定性和性能都會影響消息的時效性。

          消息的時效性是指,消息從業務方發起用設備收到的耗時。

          那么如何衡量消息的時效性呢?我們繼續往下看。

          10、如何實現消息時效性的監控與質量度量?

          傳統的消息時效性測量方法如上圖左所示:發送端和接收端在兩個設備上,在發送的時候取時間t1、在接收到消息的時候取時間t2,這兩個時間相減得到消息的耗時。

          但是這種方法并不嚴謹,為什么呢?因為這兩個設備的時間基準,很有可能是不一致的。

          我們采用的解決方案如上圖右圖所示:將發送端和接收端放在同一個設備上,這樣就可以解決時間基準的問題。我們就是基于該方案,搭建了一套撥測系統,來主動監控消息送達耗時分布。

          11、如何實現高性能、穩定的長連接網關?

          過去10年討論單機長連接性能時面對的是單機一萬連接的問題(C10K問題),而作為一個上億級設備同時在線的平臺,我們要面對的是單機100萬連接的問題。

          作為長連接網關,主要職責是維護與設備端的TCP連接及數據包轉發。

          對于長連接網關:我們應該盡可能使其輕量化。

          我們從以下幾方面進行了自上而下的重構優化:

          • 1)架構設計;
          • 2)編碼;
          • 3)操作系統配置;
          • 4)硬件特性配置。

          具體的實施方法,比如:

          • 1)調整系統最大文件句柄數、單個進程最大的文件句柄數;
          • 2)調整系統網卡軟中斷負載均衡或者開啟網卡多隊列、RPS/RFS;
          • 3)調整TCP相關參數比如keepalive(需要根據宿主機的session時間進行調整)、關閉timewait recycles;
          • 4)硬件上使用AES-NI指令加速數據的加解密。

          經過我們優化之后,線上8C32GB 的服務器可以穩定支持170萬的長連接。


          另外一大難點在于連接保活:一條端到端的 TCP連接,中間經過層層路由器、網關,而每個硬件的資源都是有限的,不可能將所有TCP連接狀態都長期保存。

          所以為了避免TCP資源,被中間路由器回收導致連接斷開,我們需要定時發送心跳請求,來保持連接的活躍狀態(為什么TCP有這樣的問題?有興趣可以讀這兩篇:《為什么說基于TCP的移動端IM仍然需要心跳保活?》、《徹底搞懂TCP協議層的KeepAlive保活機制)。

          心跳的發送頻率多高才合適?發送太快了會引起功耗、流量問題,太慢了又起不到效果,所以為了減少不必要的心跳及提升連接穩定性,我們采用智能心跳,為不同網絡環境采用差異性的頻率。

          有關長連接心跳機制的更詳細資料,可以參閱:

          12、如何實現億級設備的負載均衡?

          我們平臺超過億級設備同時在線,各個設備連接長連接網關時是通過流量調度系統進行負載均衡的。

          當客戶端請求獲取IP時,流量調度系統會下發多個就近接入網關IP:

          那么調度系統是如何確保下發的ip是可用的呢?大家可以簡單思考下。

          對于我來來說,我們采用四種策略:

          • 1)就近接入 ;
          • 2)公網探測 ;
          • 3)機器負載;
          • 4)接口成功率。

          到底采用這幾種策略呢?大家可以想下,這兩個問題:

          • 1)內網正常,公網就一定能聯通嗎?
          • 2)連接數少服務器,就一定是可用的嗎?

          答案是否定的,因為長連接網關與流量調度系統是通過內網進行心跳保活的,所以在流量調度系統上看到的長連接網關是正常的,但是很有可能長連接網關公網連接是異常的比如沒有開通公網權限等。

          所以我們需要結合多種策略,來評估節點的可用性,保障系統的負載均衡、為系統穩定性提供保障。

          13、如何滿足高并發需求?

          有這么一個場景:以每秒1000的推送速度,將一條新聞發送給幾億用戶,那么有的用戶可能是幾天后才收到這條消息,這就非常影響用戶體驗,所以高并發對消息的時效性來說是非常重要的。

          從上圖的推送流程來看:會不會覺得TiDB會成為推送的性能瓶頸?

          其實不會:初步看可能會覺得它們作為中心存儲,但因為我們采用分布式緩存,將中心存儲的數據,根據一定的策略緩存到各個業務節點,充分利用服務器資源,提升系統性能、吞吐量。我們線上的分布式緩存命中率99.9% 為中心存儲擋住了絕大部分請求,即使TiDB短時間故障,對我們影響也比較小。

          14、如何保障系統穩定性?

          14.1 概述

          作為推送平臺,平臺的流量主要分為外部調用及內部上下游之間的調用。它們大幅波動都會影響系統的穩定性,所以需要進行限流、控速,保障系統穩定運行。

          14.2 推送網關限流

          推送網關作為流量入口,其穩定性非常重要。

          要讓推送網關穩定運行,我們首先要解決流量均衡的問題即避免流量傾斜的問題。因為流量傾斜之后,很有可能會引起雪崩的情況。

          我們是采用輪詢的機制,進行流量的負載均衡,來避免流量傾斜問題。

          但是這里有兩個前提條件:

          • 1)所有推送網關節點,服務器配置要保持一致,否則很有可能會因為某個處理能力不足導致過載問題;
          • 2)應控制流入我們系統的并發量,避免流量洪峰穿透推送網關導致后端服務過載。

          我們采用的是令牌桶算法,控制每個推送網關投放速度,進而能夠對下游節點起到保護作用。

          那么令牌數量設置多少才合適呢?設置低了,下游節點資源不能充分利用;設置太高了,下游節點有可能扛不住。

          我們可以采用主動+被動的動態調整的策略:

          • 1)當流量超過下游集群處理能力時,通知上游進行限速;
          • 2)當調用下游接口超時,達到一定比例是進行限流。

          14.3 系統內部限速:標簽推送平滑下發

          既然推送網關已經限流了,為什么內部節點之間還要限速?

          這個是由于我們平臺的業務特點決定的,平臺支持全量、標簽推送,要避免性能較好的模塊,把下游節點資源耗盡的情況。

          標簽推送模塊(提供全量、標簽推送)就是一個性能較高的服務,為了避免它對下游造成影響。我們基于Redis和令牌桶算法實現了平滑推送的功能,控制每個標簽任務的推送速度,來保護下游節點。

          另外:平臺支持應用創建多個標簽推送,它們的推送速度會疊加,所以僅控制單個標簽任務的平滑推送是不夠的。需要在推送下發模塊對應用粒度進行限速,避免推送過快對業務后臺造成壓力。

          14.4 系統內部限速:消息下發時限速發送

          為了實現應用級別的限速,我們采用Redis實現分布式漏桶限流的方案,具體方案如上圖所示。

          這里我們為什么采用的是clientId(設備唯一標識),而不是使用應用ID來做一致性hash?主要是為了負載均衡。

          自從實現了這個功能之后,業務方再也不用擔心推送太快,造成自己服務器壓力大的問題。

           

          那么被限速的消息會被丟掉嗎?當然不會,我們會將這些消息存儲到本地緩存、并且打散存儲到Redis,之所以需要打散存儲主要是為了避免后續出現存儲熱點問題。

          14.5 熔斷降級

          推送平臺,一些突發事件、熱點新聞會給系統帶來較大的突發流量。我們應該如何應對突發流量呢?

           

          如上圖左所示:傳統的架構上為了避免突發流量對系統的沖擊,冗余部署大量機器,成本高、資源浪費嚴重。在面臨突發流量時,無法及時擴容,導致推送成功率降低。

          我們是怎么做的呢?我們采用增加緩沖通道,使用消息隊列和容器的解決方案(這種方案系統改動小)。當無突發流量時以較小量機器部署,當遇到突發流量時我們也不需要人工介入,它會根據系統負載自動擴縮容。

          15、基于Karate的自動化測試系統

          在日常開發中大家為了快速開發需求,往往忽視了接口的邊界測試,這將會給線上服務造成很大的質量風險。

          另外,不知道大家有沒有注意到,團隊中不同角色溝通時使用的不同媒介比如使用word、excel、xmind等,會導致溝通的信息出現不同程度折損。

          所以為了改善以上問題,我們開發了一個自動化測試平臺,用于提升測試效率與接口用例覆蓋率,我們采用領域統一的語言減少團隊中不同角色溝通信息折損。另外還可以對測試用例統一集中管理,方便迭代維護。

           

          16、內容安全

          作為推送平臺,當然要為內容安全把好關,我們提供了內容審計的能力。

          審計方法采用自動審核為主、人工審核為輔機制來提升審核效率。同時結合基于影響面及應用分級的策略進行內容審計。

          從下圖中可以看到業務請求經過接入網關轉發給內容審系統進行第一層本地規則的內容審計,如果沒有命中本地規則則調用我們諦聽系統進行內容反垃圾審計。

          17、未來規劃

          前面我們主要介紹了推送平臺這幾年的架構演進及演進過程中的系統穩定性、高性能、安全等方面的技術實踐,接下來介紹未來的重點工作。

          為了提供更易用、更穩定、更安全的推送,未來將在以下方面持續投入建設:

          • 1)在單模塊數據一致性的基礎上,實現全系統數據一致性;
          • 2)將繼續完善各系統的熔斷降級能力;
          • 3)平臺的易用性方面持續優化,提供更加便捷的平臺服務;
          • 4)建設異常流量識別的能力。

          18、演講稿附件下載

          本文內容對應的演講原稿附件下載:

           vivo推送平臺架構演進(52im.net).pdf (1.93 MB )

          演講原稿內容概覽:

          19、參考資料

          [1] Android6.0以下的雙進程守護保活實踐

          [2] Android6.0及以上的保活實踐(進程防殺篇)

          [3] 為何基于TCP協議的移動端IM仍然需要心跳保活機制?

          [4] Android版微信后臺保活實戰分享(進程保活篇)

          [5] 實現Android版微信的智能心跳機制

          [6] Android P正式版即將到來:后臺應用保活、消息推送的真正噩夢

          [7] 融云安卓端IM產品的網絡鏈路保活技術實踐

          [8] 正確理解IM長連接的心跳及重連機制,并動手實現

          [9] 史上最強Android保活思路:深入剖析騰訊TIM的進程永生技術

          [10] Android進程永生技術終極揭秘:進程被殺底層原理、APP對抗被殺技巧

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

          本文已同步發布于:http://www.52im.net/thread-4008-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
          主站蜘蛛池模板: 阳原县| 嵩明县| 辛集市| 台北市| 平原县| 丹寨县| 石棉县| 延津县| 大名县| 前郭尔| 平乐县| 闽清县| 田林县| 龙井市| 民丰县| 万安县| 崇州市| 正镶白旗| 碌曲县| 巴林右旗| 开江县| 万山特区| 尖扎县| 凤翔县| 同心县| 朝阳区| 蒲江县| 太仓市| 永年县| 堆龙德庆县| 息烽县| 慈利县| 信阳市| 长宁区| 霍林郭勒市| 巴里| 浏阳市| 鸡西市| 云梦县| 称多县| 四平市|