58同城高性能移動Push推送平臺架構演進之路

2019獨角獸企業重金招聘Python工程師標準>>> hot3.png

本文詳細講述58同城高性能移動Push推送平臺架構演進的三個階段,并介紹了什么是移動Push推送,為什么需要,原理和方案對比;移動Push推送第一階段(單平臺)架構如何設計;移動Push推送典型性能問題分析解決,以及高可用、高性能、高穩定性如何保證。

什么是移動Push推送

移動Push推送是移動互聯網最基礎的需求之一,用于滿足移動互聯環境下消息到達App客戶端。以轉轉(58趕集旗下真實個人的閑置交易平臺)為例,當買家下單后,我們通過移動Push推送消息告訴賣家,當賣家已經發貨時,我們通過移動Push消息告訴買家,讓買賣雙方及時掌握二手商品交易的實時訂單動態。

為什么需要移動Push推送?

移動互聯網絡環境下,經常會出現弱網環境,特別是2G、3G等網絡環境下,網絡不夠穩定,App客戶端和相應服務器端的長連接已經斷開,消息無法觸達App客戶端。而我們業務需要把Message(轉轉App交易消息等)、Operation(轉轉App運營活動等)、Alert(轉轉紅包未消費提醒等)等消息推送給App客戶端,從而觸發用戶看到這些消息,通過點擊這些Push消息達到相應目標。

推送原理和方案對比

移動Push推送主要有以下三種實現方式。

  1. 移動App輪詢方式(PULL)?
    App客戶端定期發起Push消息查詢請求,來達到消息推送的目的。PULL方案的優點和缺點都比較明顯,整體架構簡單但實時性較差,我們可以通過加快查詢頻率,提高實時性,但這會造成電量、流量消耗過高。

  2. 移動App基于短信推送方式(SMS Push)?
    通過短信發送推送消息,并在客戶端置入短信攔截模塊,能攔截短信,并解析后轉發給App應用處理。這個方案實時性好、到達率高,但成本很高。

  3. 移動App長連接方式(Push)?
    移動Push推送基于TCP長連接實現,消息實時性好,這是目前主流的實現方式,需要維護App客戶端和服務端的長連接心跳,會帶來額外的電量、流量消耗;在架構設計時,需要做些折中,以避免流量和電量的大量消耗。此外Push推送技術架構復雜度較高,維護移動App客戶端的海量長連接請求,并建立與App客戶端通信的加密通道,整合成內部少量有限的長連接,對通信數據進行壓縮與解壓,以節省流量。

目前移動Push推送技術基本都是結合這3個方案進行,但對于不同的移動終端平臺,又有各自不同的實現,這里詳細介紹iOS和Android平臺上的具體實現方案。

iOS平臺

對于iOS平臺,由于其特殊性,移動Push推送相對簡單,iOS應用是不允許service后臺常駐的,所以你沒有別的選擇,也沒辦法通過開發自己的Push service來完成推送下發,只能通過蘋果APNS方式來完成。iOS移動Push推送流程如圖1所示。
140540_88R0_2004011.png

圖1 iOS移動PUSH推送流程

Android平臺

在Android平臺上,由于對service常駐沒有限制,可用的方案就多一些:可以通過Google官方C2DM 完成、開源方案(例如XMPP)、借助第三方,或者完全自主研發的移動Push推送方案。?
Google C2DM的主要流程如圖2所示。
140553_1IgP_2004011.png

圖2 C2DM移動PUSH推送流程

Google C2DM和Apple APNS流程大致類似,但其最大的問題是移動Push推送服務器在國外,很容易被屏蔽,而且Push推送延遲較大。此外由于 Android社區分裂比較嚴重,很多廠商直接就把C2DM模塊給去掉了,所以在國內這個方案極不可靠,變成了一個理論上的方案。

移動Push推送開源方案

對于開源移動Push推送協議,常見的有XMPP等, 事實上Google的C2DM底層也是基于XMPP協議實現的,我們通過線下測試發現,開源移動Push推送方案主要有兩個問題:第一,沒有ACK機制,消息到達沒有保證,不可靠;第二,當移動Push消息請求量并發增大時,系統開始變得不穩定,甚至出現了模塊宕機的情況。因此直接使用移動Push推送開源方案,也不是非常可靠,我個人建議:在大規模使用開源的移動Push推送方案之前,必須做到對開源技術方案整體把握住,不然一旦出現問題,無法及時定位和修復的話,帶來的后果將會是災難性的。

借助第三方移動Push推送方案

除此之外,目前移動Push推送市場上,還有不少第三方推送產品可供選擇,但需要面臨以下幾個問題:

  • 到達率?
    雖然第三方移動Push推送產品都宣傳到達率高于90%,但是實際使用起來,發現遠遠達不到。當然到達率低的問題,除了第三方移動Push推送平臺本身技術原因外,還和業務推送方的用戶選取有很大關系,如果用戶較活躍,到達率就會高些,如果用戶不活躍,或者用戶已經卸載了相應的App客戶端,必然造成到達率進一步降低。

  • 實時性&控制度?
    第三方移動Push推送產品的推送通道是共用的,會面向多個推送客戶,如果某一個客戶Push推送量特別大,那么其他的消息實時性可能就會受到影響,這些都是業務推送方不可控的,會比較被動。

完全自主研發的移動Push推送方案

我們曾經考慮實現一套完全自主的移動Push推送平臺,如果從零開始來做,需要解決幾個難點:第一,移動Push推送服務端對移動App客戶端海量長連接的維護管理。第二,App客戶端常駐 service穩定性,如何使Push service常駐?我們可以借助父子進程互相監控的方式來做到,一旦發現對方進程不在了,會重新建立,繼續循環監控。第三,手機內存不足時,系統會殺掉Push service,甚至有些操作系統比較強勢,它會向iOS系統一樣并不允許第三方Push service 常駐。第四,移動Push推送到達率的提高,除了技術手段外,還有一些PR的手段,比如移動App客戶端Push service通過在相應操作系統上添加白名單的方式使其永久常駐。總之,在移動互聯復雜的場景下如何讓移動Push推送到達率變得更高,不是一件簡單的事兒。

58同城移動Push推送方案

我們綜合考慮前面講述的開源、基于第三方、完全自主研發方案,58同城并沒有選擇從零開始完全自主研發而是采用了基于第三方移動Push推送平臺和自主研發高性能Provider的方案(如圖3所示),滿足每天百億量級的吞吐量,并通過動態組合和擴展的方式,結合離線的移動Push推送數據分析,不同手機使用不同的推送策略,針對性地優化。在Android平臺,我們融合多種第三方移動Push平臺,從而有效提升到達率。
140914_NIqf_2004011.jpg

圖3 58同城移動PUSH推送平臺技術架構

第一階段(單平臺):架構如何設計

背景&需求

2011年我們研發了58幫幫,這是一款滿足58用戶和商戶之間溝通的即時通訊軟件,用戶間可以互相添加好友、收發消息等。58幫幫的消息推送基于App客戶端和服務器的長連接,一旦這條長連接斷開,那么IM服務端的消息將無法推送給App客戶端,用戶也無法看到這些消息。在iOS平臺上,58幫幫App切換到后臺后,App與IM的長連接斷開,消息無法觸達,這時候我們需要借助iOS APNS機制,IM消息需要發送給APNS,APNS再轉發對應的消息到58幫幫App。Android切換至后臺,App與IM的長連接保持,IM消息可以正常推送,因此在這個階段我們需要解決的問題是在iOS平臺上,當58幫幫App切后臺后,IM在長連接斷開后的消息觸達需求。

設計目標

基于上述的背景和需求,我們在設計移動Push推送第一階段(單平臺)架構時,首先要滿足在iOS平臺上,當IM長連接斷開后,IM消息的能夠觸達到App客戶端。其次我們的移動Push推送協議設計也具備很好的擴展性,在可以預見的未來,Push推送平臺將逐步接入更多的App,因此我們設計目標iOSProvider是一個通用的iOS推送服務。不同App通過使用不同的移動Push推送證書借助同一iOSProvider完成移動Push消息推送,對于不同App的接入,我們采用了配置文件方式動態擴展接入,iOSProvider根據所配置App證書與APNS建立并維護多條TSL連接。配置文件的格式如下:
141124_obO8_2004011.png

其中,第一個域為推送服務類型Type,以備擴展,1為APNS;第二個域為內部定義的APPID號,對應服務的App;第三個域為App的Apple證書文件名;第四個域為與APNS建立的連接數;?
每個App接入的配置為一行,舉例如下:
141132_HDEH_2004011.png

除此之外,iOSProvider需要對每個接入App的APNS連接池進行管理,動態增刪TSL連接,具備動態重連機制,并具有單獨的反饋接收線程,用于異步接收APNS返回無效的Token,反饋給移動Push推送業務方,用于下次移動Push消息推送的優化。iOSProdiver根據Type、APPID選擇對應的APNS連接,通過推送線程組裝APNS包發送到APNS服務器,如圖4所示。

141142_nxky_2004011.png

圖4 iOSProvider架構圖

第二階段(多平臺):架構如何設計優化

隨著移動互聯時代的到來,58同城研發了多個App,每一個App都有移動Push消息推送的需求(消息、運營活動、過期提醒等),并且每一款App同時具有多個終端:Android版、iOS版等。在這樣的需求背景下,我們的移動Push推送平臺需要繼續演進,如何演進呢?

iOS移動Push推送通道可以很好的滿足業務推送需求,但目前還不具備Android移動Push推送的能力,因此我們急需要研發Android移動Push推送通道。如何做?綜合目前可選擇的方案,我們選擇了基于第三方推送平臺以及自主研發高性能AndroidProvider的方案。

首先重點講述針對Android移動Push推送的流程:第一,App客戶端向第三方移動Push推送平臺注冊,獲取對應的App唯一標示(Token)。第二,App將Token信息發送給AndroidProvider并集中存儲,以便后續基于Token的移動Push推送。第三,AndroidProvider通過HTTPS或者TSL的方式和第三方移動Push推送平臺建立連接,并把需要推送的消息發送到第三方移動Push推送平臺。第四,第三方移動Push推送平臺收到AndroidProver推送的消息后,會把此消息及時推送到App,從而完成整個推送過程,如圖5所示。
141203_3WEJ_2004011.png

圖5 Android移動PUSH推送流程

AndroidProvider子系統整體結構分為四個層次,第一層為業務方移動Push推送接入,用于眾多移動Push推送業務方的接入。第二層為網絡交互層,用于接收移動Push推送業務方的消息數據以及發送請求處理層的處理數據給業務推動調用。第三層為請求處理層,用于處理網絡交互層放入請求隊列的數據,組裝成第三方移動Push推送接口需要的數據,通過HTTP或者HTTPS的方式調用下游的接口,并等待請求結果的返回,把請求返回的結果放入回應隊列。第四層為第三方移動Push推送平臺,由第三方提供,開放給使用方接口,供調用其功能,如圖6所示。
141219_z1ta_2004011.png

圖6 AndroidProiver系統架構圖

隨著越來越多的移動App接入,移動Push推送需求趨向多樣化,同時移動Push推送業務邏輯復雜化(多終端、批量發送、業務規則多樣),公共策略每個業務方重復開發(深夜防打擾功能、發送頻率和發送速率的限制等),造成開發效率低下。為了解決這些問題,我們抽象了公共的邏輯,并進行了統一的封裝,對業務調用方透明,這些公共的邏輯包括:通用的策略和通用的控制,如圖7所示。
141239_V46W_2004011.png

圖7 Android移動PUSH推送演進業務架構

在移動Push推送第二階段(多平臺)階段,我們具備了Android、iOS的通道服務能力,滿足推送消息的需求。但是我們沒有提供統一的發送接口,業務方需要各自組包(Android、iOS)發送不同的推送通道,除此之外,推送通道性能方面還有待提升,推送通道穩定性還有待提升,此外推送通道包含了相對共同的業務邏輯,推送通道還不夠“純粹”。

第三階段:架構和協議如何設計和優化

移動Push推送第二階段還存在一系列的問題,因此在第三階段需要解決,并且隨著更多App接入,我們需要提供公司級統一的高性能移動Push推送平臺。基于第三方移動Push推送平臺,我們自主研發了滿足每天推送百億量級的高性能Provider,推送平臺具備了高穩定性、接入方便,并提供了較高的推送到達率。

移動Push推送平臺第三階段我們如何架構和設計?首先我們滿足對下游接入方多種連接的管理(HTTP、HTTPS、TCP、SSL、TSL),具備了多種連接動態伸縮性,從而滿足Provider層對移動Push推送連接的要求。其次平臺要具備高并發的特性,通過完全異步的設計和多線程支持,做到了高并發和支持10萬QPS吞吐量。再次我們需要對接入下游的錯誤進行處理,一旦發現連接被斷開等錯誤后,要能夠自動使用新的連接,并且對已經發出還沒到達App客戶端的推送消息進行重發,以保證消息不丟失。第四我們需要對通道進行封裝,對外提供統一的友好接入接口,屏蔽底層iOS和Android接入的差異性。最后在Android移動推送方面,我們接入了更多的第三方推送平臺,以達到更高的到達率。

基于這些方面的考慮,58同城移動Push推送平臺采用了低耦合的分層架構設計(如圖3所示),分為三層Push Entry、Push Transfer、Provider(iOSProvider和AndroidProvider)。其中Push Entry是業務方調用的入口,我們采用異步消息隊列的方式,提供了較高的業務方發送的速度,并且具備了消息緩沖的功能,使得高峰期的海量移動Push消息推送對整個平臺沖擊較少,也起到了保護推送系統的作用。Push Transfer會從Push Entry層接收消息進行解析,對推送消息進行合法性檢查,如果格式不合法,直接丟棄,同時會進行接收到的推送消息格式轉換成內部的消息格式,分平臺轉發到iOSProvider或者AndroidProvider上;provider接收到Push Transfer的消息后,會按照下游需要的消息格式(APNS協議、Android協議)進行轉換,進行消息的下發,在下發的過程中,會進行消息的重發,以確保消息下發到第三方推送平臺。

Provider模塊內部如何設計?以iOSProvider為例,它分為三個層次:接入邏輯、業務邏輯、APNS出口。其中接入邏輯主要處理網絡交互和請求分發;業務邏輯主要處理線程分裂擴展、并發處理和錯誤處理;APNS出口處理向APNS的發送邏輯,如圖8所示。
141259_NsIG_2004011.png

圖8 iOSProvider模塊結構圖

對于移動Push推送平臺來說,追求達到率是我們最核心的指標,沒有之一。因此在Android方面,我們融合了多個第三方推送平臺,通過機型控制,對不同的機型使用不同通道,進一步提升推送到達率。AndroidProvider層進行消息推送策略的控制,先推送一通道,根據此推送通道ACK情況,是否繼續推送其他通道。推送多個Push通道,會出現推送消息重復到達App客戶端的情形,此時需要App客戶端根據推送消息ID進行去重,收到的重復推送消息忽略處理。

典型性能問題分析解決以及高可用、高性能、高穩定性如何保證

在移動Push推送不斷演進的過程中,我們遇到了AndroidProvider并發低的問題,仔細分析,是因為我們采用HTTPS庫,由于庫中HTTPS的連接實現不是線程安全的,對每個HTTPS的請求都加鎖串行化處理,以保證線程的安全性。發現問題后,我們通過在線上增加多進程部署的方式暫時解決,使得我們有足夠的時間分析此問題產生的根本原因。經過深入分析,發現原因是我們對HTTPS的庫掌握不夠,導致加鎖粒度過大,通過HTTPS庫提供的更小粒度的鎖,我們不僅解決了線程不安全的問題,也提升了AndroidProvider的并發度,如圖9所示。

141310_uTwZ_2004011.png

圖9 HTTPS庫細粒度鎖實現方式

總之,58同城統一的高性能移動Push推送平臺通過無狀態化設計和冗余部署等方式確保了推送平臺的高可用,通過純異步、動態多線程的支持提供推送平臺的高性能,通過質量保證、多種監控機制(進程監控、語義監控、錯誤日志監控、數據波動監控等),有問題及時發現處理保證了推送平臺的高穩定性。

最后,我要感謝項目組的同學,特別感謝姚勁同學,有了你們持續不斷的努力和付出,才有了今天這篇文章;也感謝老婆大人,有你在背后默默的支持,才有了今天這篇文章。


孫玄:58趕集集團系統架構師,技術負責人,技術委員會架構組主任,也是58同城即時通訊、C2C技術負責人,負責58核心系統的架構以及優化工作。分布式系統存儲專家,前百度高級工程師,參與社區搜索部多個基礎系統的設計與實現。

本文為《程序員》原創文章,未經允許不得轉載,訂閱2016年《程序員》請點擊?http://dingyue.programmer.com.cn

轉載于:https://my.oschina.net/agileai/blog/630299

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/459076.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/459076.shtml
英文地址,請注明出處:http://en.pswp.cn/news/459076.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Android Studio 插件

https://plugins.jetbrains.com/?androidstudio轉載于:https://www.cnblogs.com/xiaoyao095/p/4712552.html

resin指定java版本_resin的幾個常用配置

本文著重介紹resin的幾個常用配置注:1. 本文并非resin.conf的使用說明,只是從實用角度出發,按需分析常用的配置。更多的內容到官網學習吧;2. 基于resin3.x的探討。(友情提示:剛下載的resin中,resin.conf里的…

Android生命周期函數執行順序

轉載自:http://blog.csdn.net/intheair100/article/details/39061473 程序正常啟動:onCreate()->onStart()->onResume(); onCreate之后就會執行onStart,此時畫面已經顯示出來了,但還不能交互;然后執行onResume,此時可以與用…

hdoj-2028-Lowest common multiple plus

題目&#xff1a;Lowest common multiple plus 代碼&#xff1a; #include<stdio.h> int common(int a,int b)//計算最大公約數 {int ca%b,t0;if(b>a){tb;ba;at;}while(a%b!0){ca%b;ab;bc;}return b; } int q[105]; int main() {int n,i,j,t0;while(scanf("%d&qu…

C#系統命名空間

*************************************************** 更多精彩&#xff0c;歡迎進入&#xff1a;http://shop115376623.taobao.com *************************************************** 一、基礎命名空間 System.Collections 包含了一些與集合相關的類型,比如列表,隊列,位數…

java不規則算法_分布式id生成算法 snowflake 詳解

背景在復雜分布式系統中&#xff0c;往往需要對大量的數據和消息進行唯一標識。如在支付流水號、訂單號等&#xff0c;隨者業務數據日漸增長&#xff0c;對數據分庫分表后需要有一個唯一ID來標識一條數據或消息&#xff0c;數據庫的自增ID顯然不能滿足需求&#xff0c;此時一個…

Android中下載、安裝和卸載(原)

應用場景&#xff1a;在檢查版本更新的時候經常需要從服務器端下載然后安裝到手機中 使用工具&#xff1a; XUtils&#xff0c;這個開源的框架真的是需要花大把時間去閱讀和理解的&#xff0c;十分有用的&#xff0c;on the way &#xff01; fighting&#xff01; 下載&#x…

Android加載圖片OOM錯誤解決方式

前幾天做項目的時候&#xff0c;甲方要求是PAD &#xff08;SAMSUNG P600 10.1寸 2560*1600&#xff09;的PAD上顯示高分辨率的大圖片。 SQLITE採用BOLD方式存儲圖片&#xff0c;這個存取過程就不說了哈&#xff0c;網上一大堆。 可是在加載/讀取/顯示圖片的時候會報OOM錯誤&am…

C# 對Ini文件操作(C# ini文件操作類)

*************************************************** 更多精彩&#xff0c;歡迎進入&#xff1a;http://shop115376623.taobao.com *************************************************** /* C# 對Ini文件操作&#xff08;C# ini文件操作類&#xff09; [IniFiles.cs] 蝶曉…

python對文件進行讀寫操作

2019獨角獸企業重金招聘Python工程師標準>>> python進行文件讀寫的函數是open或file file_handler open(filename,,mode&#xff09; Table mode 模式描述r以讀方式打開文件&#xff0c;可讀取文件信息。w以寫方式打開文件&#xff0c;可向文件寫入信息。如文件存在…

android:contentDescription

android:contentDescription這個屬性相信大家并不陌生&#xff0c;在ImageButton的使用過程中如果不添加這個屬性會有警告信息。 那么android:contentDescription究竟是干什么的呢&#xff1f;今天查了下資料才知道這個屬性的真正作用。 該屬性為視力障礙的用戶提供方便&#x…

c#中bin,obj,properties文件夾的作用

*************************************************** 更多精彩&#xff0c;歡迎進入&#xff1a;http://shop115376623.taobao.com *************************************************** Bin目錄用來存放編譯的結果&#xff0c;bin是二進制binrary的英文縮寫&#xff0c;因為…

getAttribute實例例java_Java ExifInterface.getAttribute方法代碼示例

import android.media.ExifInterface; //導入方法依賴的package包/類public static void copyExif(ExifInterface originalExif, int width, int height, String imageOutputPath) {String[] attributes new String[]{ExifInterface.TAG_APERTURE,ExifInterface.TAG_DATETIME,…

檢測SDWebImage有沒有緩存圖片 IOS 獲取網絡圖片大小

判斷圖片是否緩存NSURL *url [NSURL URLWithString:[model.content objectForKey:"image"]];//請求網絡地址數據的同步方法//因為這個方法在子線程(全局隊列)中執行,所以不需要考慮死線程的問題SDWebImageManager *manager [SDWebImageManager sharedManager];[man…

mac 下 使用 java運行 class 文件 總是提示 “錯誤: 找不到或無法加載主類”的解決方法...

發現問題 切換到mac平臺后&#xff0c;突然想寫點程序運行在mac下&#xff0c;想到mac自帶java&#xff0c;會方便好多。不過在這過程中遇到了麻煩&#xff1a; 總是提示 “錯誤: 找不到或無法加載主類” 工程結構 查了好久&#xff0c;終于找到原型所在&#xff0c;發現網上很…

[轉]VisualStudio如何組織解決方案的目錄結構

*************************************************** 更多精彩&#xff0c;歡迎進入&#xff1a;http://shop115376623.taobao.com *************************************************** 解決方案與項目&#xff1a; 從VC6之后VC系列就使用解決方案&#xff08;Solution&…

java幾種刪除_幾種刪除Linux目錄的方法

在Linux中有很多方法可以刪除目錄&#xff0c;在圖形化界面可以利用文件管理器&#xff0c;或者通過終端刪除。本文將介紹在文本界面使用命令刪除目錄。使用rmdir刪除目錄Rmdir命令間成“remove directory”&#xff0c;用于刪除空目錄的命令。例如&#xff0c;刪除一個名為“M…

php公鑰模數,php – 如何從公共指數和RSA模數生成DER / PEM證書?

眾所周知,公鑰由公共指數和模數組成.我的問題是&#xff1a;如何從公共指數和RSA模數生成DER / PEM證書&#xff1f;非常感謝你提前.解決方法:使用公共指數和模數,你可能希望做的最好的事情是得到這樣的東西&#xff1a;-----BEGIN PUBLIC KEY-----MIGGAoGAfHlcdrcuOK6C02rbGR3…

C# DataTable的詳細用法

*************************************************** 更多精彩&#xff0c;歡迎進入&#xff1a;http://shop115376623.taobao.com *************************************************** DataTable 是一個臨時保存數據的網格虛擬表(表示內存中數據的一個表。)。DataTable是A…

【SpringMVC】SpringMVC系列6之@CookieValue 映射請求Cookie 值

6、CookieValue 映射請求Cookie 值 6.1、示例 CookieValue 可讓處理方法入參綁定某個 Cookie 值&#xff0c;示例如下&#xff1a;