vivo 推送系統(tǒng)的容災(zāi)建設(shè)與實踐
作者:vivo 互聯(lián)網(wǎng)服務(wù)器團隊 - Yu Quan
本文介紹了推送系統(tǒng)容災(zāi)建設(shè)和關(guān)鍵技術(shù)方案,以及實踐過程中的思考與挑戰(zhàn)。
一、推送系統(tǒng)介紹
vivo推送平臺是vivo公司向開發(fā)者提供的消息推送服務(wù),通過在云端與客戶端之間建立一條穩(wěn)定、可靠的長連接,為開發(fā)者提供向客戶端應(yīng)用實時推送消息的服務(wù),支持百億級的通知/消息推送,秒級觸達移動用戶。
推送系統(tǒng)主要由接入網(wǎng)關(guān),邏輯推送節(jié)點,長連接組成,長連接負責(zé)與用戶手機終端建立連接,及時把消息送達到手機終端。
推送系統(tǒng)的特點是并發(fā)高、消息量大、送達及時性較高。
vivo推送系統(tǒng)現(xiàn)狀最高推送速度140w/s,單日最大消息量200億,端到端秒級在線送達率99.9%。同時推送系統(tǒng)具備不可提前預(yù)知的突發(fā)大流量特點。針對推送系統(tǒng)高并發(fā),高時效,突發(fā)流量等特點,如何保證系統(tǒng)可用性呢?本文將從系統(tǒng)架構(gòu),存儲容災(zāi),流量容災(zāi)三個方面進行講述,推送系統(tǒng)是如何做容災(zāi)的。
二、系統(tǒng)架構(gòu)容災(zāi)方案
2.1 長連接層容災(zāi)
長連接是推送系統(tǒng)最重要的部分,長連接的穩(wěn)定性直接決定了推送系統(tǒng)的推送質(zhì)量和性能,因此,需要對長連接層做好容災(zāi)和實時調(diào)度能力。
原有推送系統(tǒng)架構(gòu)是長連接層都部署在華東,所有vivo IDC邏輯節(jié)點通過VPC與華東的Broker建立連接,手機端跟華東的broker進行長連接通信。這種部署方式存在以下問題。
問題一:華北、華南手機都需要連接華東的Broker,地域跨度大,長連接網(wǎng)絡(luò)穩(wěn)定性和時效性相對較差。
問題二:邏輯層跟華東的Broker之間由一條VPC連接,隨著業(yè)務(wù)的發(fā)展,推送流量越來越大,帶寬會出現(xiàn)瓶頸,有超限丟包的風(fēng)險。另外當(dāng)該VPC出現(xiàn)故障時,會造成全網(wǎng)消息無法送達。
注:長連接層節(jié)點名為Broker。
原始長連接架構(gòu)圖:

基于以上架構(gòu)存在問題,進行了優(yōu)化,將Broker進行三地部署,分別部署在華北,華東,華南。
華北、華東、華南三地用戶采用就近接入方式。
優(yōu)化后的架構(gòu),不僅可以保證長連接網(wǎng)絡(luò)穩(wěn)定性和時效性。同時具有較強的容災(zāi)能力,華東,華南Broker通過云網(wǎng)跟華北Broker連接,華北Broker通過VPC與vivo IDC連接。當(dāng)華北、華東、華南某個地區(qū)Broker集群故障或者公網(wǎng)故障,不會影響到全網(wǎng)設(shè)備收發(fā)消息。但是這種方式還是存在一個問題,就是某個地區(qū)Broker集群故障或者公網(wǎng)故障,會出現(xiàn)該區(qū)域部分設(shè)備無法收到推送消息的情況。
三地部署后的架構(gòu)圖:

針對上述單個地區(qū)異常導(dǎo)致該區(qū)域部分設(shè)備無法收到推送消息的問題,我們設(shè)計了一套流量調(diào)度系統(tǒng),可以做到實時流量調(diào)度和切換。global scheduler節(jié)點負責(zé)策略調(diào)度和管理。
vivo phone進行注冊時,dispatcher會下發(fā)多個地區(qū)的ip地址,默認情況下,進行就近連接。單多次連接失敗后,嘗試連接其他ip。當(dāng)某個地區(qū)Broker出現(xiàn)長連接數(shù)瓶頸或者VPC出現(xiàn)故障,可以通過global scheduler節(jié)點下發(fā)策略,讓該故障地區(qū)的設(shè)備重新從dispatcher獲取新的ip集的ip,與其他地區(qū)Broker建立長連接,邏輯節(jié)點下發(fā)消息到重連后的Broker。等到該地區(qū)恢復(fù)后,可以重新再下發(fā)策略,進行回調(diào)。
流量調(diào)度系統(tǒng)圖:

2.2 邏輯層容災(zāi)
長連接層做好容災(zāi)后,邏輯層也需要做相應(yīng)容災(zāi)。之前我們邏輯層都部署在一個機房,不具備機房機容災(zāi)能力,當(dāng)一個機房出現(xiàn)斷電風(fēng)險,會出現(xiàn)服務(wù)整體不可用問題,因此我們做"同城雙活"部署方案改造。
邏輯層單活架構(gòu):

邏輯層分別在vivo IDC1和vivo IDC2進行部署,網(wǎng)關(guān)層根據(jù)路由規(guī)則將流量按照一定比例分別下發(fā)到兩個IDC,實現(xiàn)邏輯層同城雙活。我們發(fā)現(xiàn),數(shù)據(jù)中心還是只有一個,部署在vivo IDC1,根據(jù)成本、收益,以及多數(shù)據(jù)中心數(shù)據(jù)同步延遲問題綜合考慮,數(shù)據(jù)中心暫時還是以單數(shù)據(jù)中心為主。
邏輯層雙活架構(gòu):

三、 流量容災(zāi)方案
做好系統(tǒng)架構(gòu)的容災(zāi)能力后,推送系統(tǒng)的網(wǎng)關(guān)層還需要應(yīng)對突發(fā)流量做相應(yīng)的應(yīng)對措施,做好流量控制,保證系統(tǒng)穩(wěn)定性。歷史上,我們曾經(jīng)因為熱點和突發(fā)新聞事件,并發(fā)推送流量巨大,導(dǎo)致服務(wù)出現(xiàn)異常,可用性降低問題。
如何應(yīng)對突發(fā)大流量,保證突發(fā)流量的情況下,系統(tǒng)可用性不變,同時能兼顧性能和成本。為此,我們分別對比了設(shè)計了以下兩種方案。
常規(guī)的方案是一般是根據(jù)歷史情況估算冗余部署大量機器,來應(yīng)對突發(fā)流量。單這種方式成本較高,突發(fā)流量可能只持續(xù)5分鐘或更短時間,而系統(tǒng)為了滿足5分鐘突發(fā)流量,需要冗余部署大量機器。一旦流量超過了部署機器可承擔(dān)的上限,無法及時擴容,可能導(dǎo)致可用性下降,甚至出現(xiàn)雪崩效應(yīng)。
傳統(tǒng)方案下的推送架構(gòu):

那如何設(shè)計一套既可以控制成本,面對突發(fā)大流量彈性擴容,又保證消息不漏并兼顧推送性能的方案呢?
優(yōu)化方案:在原有架構(gòu)的基礎(chǔ)上,在接入層增加緩沖通道,當(dāng)流量洪峰到來時,對于系統(tǒng)可處理的上限能力外的流量,打入緩沖隊列。通過消息隊列形式,增加bypass接入層,限速消費消息隊列。在流量洪峰過去后,提升bypass消費速度,處理緩存隊列消息。bypass接入層通過docker部署,支持動態(tài)擴縮容,默認最小化集群,當(dāng)消息隊列積壓很多,并且下游有能力處理時,提升消費速度,bypass根據(jù)CPU負載動態(tài)擴容,快速消費消息隊列。處理完畢后動態(tài)縮容。
消息隊列:選用吞吐量較大的KAFKA中間件,并且與離線計算KAFKA集群共用,能充分利用資源。
bypass接入層:采用docker部署,支持根據(jù)CPU負載和時間動態(tài)擴縮容。默認最小集群部署。對于已知的流量高峰時段,可以提前擴容服務(wù),保證流量快速處理。未知時段流量高峰,可以bypass接入層,根據(jù)CPU負載情況進行動態(tài)擴縮容。
增加緩存隊列后的推送架構(gòu):

進行上述改造后,還存在一個問題,就是如何進行接入層全局控速。我們采用的方式是收集下游推送節(jié)點的推送流量情況,比如:流量達到系統(tǒng)可承受上限的80%時下發(fā)限速指令,調(diào)整接入層推送速度。讓消息先積壓在消息隊列,等到下游流量降低之后,下發(fā)解除限速指令,讓bypass接入層加速消費消息隊列,進行推送。
增加控速后的推送架構(gòu):

優(yōu)化后方案與傳統(tǒng)方案對比:

四、存儲容災(zāi)方案
做好并發(fā)流量控制后,能很好的預(yù)發(fā)突發(fā)熱點問題。推送系統(tǒng)內(nèi)部,由于使用Redis集群緩存消息,出現(xiàn)過因為Redis集群故障導(dǎo)致消息無法及時送達問題。因此,我們考慮對Redis集群做相關(guān)容災(zāi)方案設(shè)計,實現(xiàn)系統(tǒng)在Redis集群故障期間,也能及時推送消息并保證消息不丟失。
推送消息體緩存在Redis集群中,推送時從Redis中獲取消息體,如果Redis集群宕機,或者內(nèi)存故障,會導(dǎo)致離線消息體丟失。
原有消息流程:

方案一:引入另一個對等Redis集群,采用推送雙寫方式,雙寫兩個Redis集群。該方案需要冗余部署規(guī)模對等的備Redis集群。推送系統(tǒng)需要雙寫Redis操作。

方案二:原有Redis集群,采用RDB+AOF方式同步到另一個備Redis集群。該方案不在需要推送系統(tǒng)雙寫Redis改造,直接利用將原有Redis集群數(shù)據(jù)同步到另一個備Redis集群。也需要冗余部署規(guī)模對等的備Redis集群。可能存在部分?jǐn)?shù)據(jù)同步延遲導(dǎo)致推送失敗問題。

方案三:應(yīng)用另一個分布式存儲系統(tǒng),磁盤KV,兼容Redis協(xié)議,同時具有持久化能力??梢员WC消息體不丟失。但是為了節(jié)省成本,不再直接使用Redis集群對等資源。而是根據(jù)推送特點,推送分為單推、群推。單推是一對一推送,一個用戶一條消息體。群推是一對多推送,一個消息體對應(yīng)多個用戶。群推往往是任務(wù)級別推送。因此我們使用一個相對小一些的磁盤KV集群,主要用于冗余存儲,群推消息體,即任務(wù)級別的消息。對于單推,還是只保存到Redis中,不進行冗余存儲。
如果Redis集群故障,對于單推消息,推送系統(tǒng)可以攜帶消息體往下游推送,確保消息可以繼續(xù)下發(fā)。對于群推消息,因為消息體冗余存儲在磁盤KV中,當(dāng)Redis集群故障后,可以降級到讀取磁盤KV。

方案三還存在一個問題,就是磁盤KV的寫入性能和Redis集群不是一個數(shù)量級,特別是時延,磁盤KV在平均在5ms左右。而Redis集群卻在0.5ms。如果在推送系統(tǒng)對群推消息體進行雙寫。這個時延是不能接受的。因此只能采用異步寫入磁盤KV的方式。這里將備份群推消息體,先寫入消息中間件KAFKA,由bypass節(jié)點消費KAKFA進行異步寫入磁盤KV。這樣在使用的災(zāi)備磁盤KV資源較少的前提下,保證推送系統(tǒng)的高并發(fā)能力,同時可以保證群推消息體不丟失,Redis異常時,單推消息攜帶消息體推送,群推消息體讀取磁盤KV。

存儲容災(zāi)方案對比:

五、總結(jié)
本文從系統(tǒng)架構(gòu)容災(zāi)、流量容災(zāi)、存儲容災(zāi)三個方面講述了推送系統(tǒng)容災(zāi)建設(shè)過程。系統(tǒng)容災(zāi)需要根據(jù)業(yè)務(wù)發(fā)展,成本收益,實現(xiàn)難度等多方面考慮。
當(dāng)前我們長連接層已具備三地部署,邏輯層具備同城雙活,數(shù)據(jù)中心為單數(shù)據(jù)中心。后續(xù)我們會持續(xù)研究和規(guī)劃雙數(shù)據(jù)中心,兩地三中心部署架構(gòu)方式來逐步加強推送系統(tǒng)容災(zāi)能力。