小米海量數(shù)據(jù)推送服務(wù)技術(shù)講解
11.11大促,隨著移動(dòng)端業(yè)務(wù)量的急劇提升,像小米推送這樣的基礎(chǔ)服務(wù)也經(jīng)受了巨大的考驗(yàn)。11月12日,小米的項(xiàng)目總監(jiān)汪軒然在微博上宣布,“小米推送服務(wù)共發(fā)出9.65億條消息,平均每分鐘發(fā)送67萬條。更值得一提的是,后臺(tái)監(jiān)控顯示,推送服務(wù)后臺(tái)系統(tǒng)在全天運(yùn)作非常平穩(wěn),沒有任何卡頓擁堵現(xiàn)象,讓各種促銷、返利、訂單更新消息***時(shí)間觸達(dá)用戶。”
汪軒然,2007年畢業(yè)于清華大學(xué)計(jì)算機(jī)系,后加入微軟亞洲工程院,曾參與WP7上的瀏覽器的開發(fā)。2010年7月加入小米,曾擔(dān)任米聊安卓團(tuán)隊(duì)的團(tuán)隊(duì)主管,現(xiàn)在在小米任項(xiàng)目總監(jiān),負(fù)責(zé)小米的開發(fā)者服務(wù),掌管推送服務(wù)、統(tǒng)計(jì)服務(wù)和移動(dòng)廣告聯(lián)盟三大業(yè)務(wù),旨在為小米搭建一個(gè)移動(dòng)App業(yè)務(wù)的互聯(lián)網(wǎng)生態(tài)圈。
我們聯(lián)系了汪軒然,就小米推送服務(wù)的架構(gòu)、特點(diǎn)、性能等問題對(duì)他進(jìn)行了采訪,以下內(nèi)容根據(jù)本次采訪整理而成。
基礎(chǔ)技術(shù)架構(gòu)
協(xié)議是推送服務(wù)的核心。小米推送服務(wù)所采用的協(xié)議是由之前的米聊演變過來的,而米聊從一開始就選擇使用XMPP協(xié)議,之后開發(fā)團(tuán)隊(duì)對(duì)XMPP協(xié)議做過幾輪精簡(jiǎn)和重構(gòu)?,F(xiàn)在XMPP部分只是作為一個(gè)數(shù)據(jù)的傳輸層,之上跑著各種獨(dú)立的業(yè)務(wù),每個(gè)業(yè)務(wù)稱為一個(gè)“channel”;每個(gè)channel上跑的數(shù)據(jù)格式可以是不一樣的。消息推送服務(wù)是其中一個(gè)channel,這個(gè)channel上傳輸?shù)臄?shù)據(jù)是通過Thrift進(jìn)行二進(jìn)制化的協(xié)議格式。
再來看一下小米推送服務(wù)的服務(wù)端架構(gòu)。下圖是后臺(tái)服務(wù)端的一個(gè)基本架構(gòu)圖。整個(gè)服務(wù)端包含如下幾層:
- XMPP前端:用于維護(hù)跟客戶端之間的長(zhǎng)連接,使用EJabberd項(xiàng)目來處理來自客戶端的XMPP請(qǐng)求,同時(shí)通過XMQ模塊來處理推送服務(wù)特有的XMPP消息協(xié)議。
- 中間層:業(yè)務(wù)邏輯層,主要用于將消息請(qǐng)求異步化、創(chuàng)建和維護(hù)消息隊(duì)列、以及處理客戶端的一些命令請(qǐng)求(注冊(cè)、設(shè)置別名、設(shè)置topic等)。
- HTTP前端:這一層負(fù)責(zé)對(duì)接來自第三方App的服務(wù)器的發(fā)消息的HTTPS請(qǐng)求,以及來自客戶端生成賬號(hào)的HTTPS請(qǐng)求。
再就是數(shù)據(jù)存儲(chǔ),這里采用了小米的統(tǒng)一HBase存儲(chǔ),同時(shí)還使用MySQL來保存一些量不大,但需要復(fù)雜過濾條件的數(shù)據(jù)(topic等),并且為了降低對(duì)HBase的壓力,中間還加了一層Redis作為緩存。
***看一下客戶端架構(gòu)??蛻舳薙DK主要包含兩個(gè)層次:SDK層和PushService層。前者提供了面向App接入的接口、回調(diào)方法以及對(duì)Thrift的數(shù)據(jù)進(jìn)行反序列化的處理邏輯;后者用于維護(hù)XMPP長(zhǎng)連接和收發(fā)消息。兩層之間使用Intent方式來傳輸數(shù)據(jù)。值得一提的是,在MIUI系統(tǒng)上,PushService層是系統(tǒng)共用的,即MIUI系統(tǒng)提供了一個(gè)統(tǒng)一的PushService管理模塊,不需要每個(gè)應(yīng)用單獨(dú)啟動(dòng)自己的PushService。
功能實(shí)現(xiàn)
小米推送服務(wù)支持單發(fā)和群發(fā)消息兩種推送方式。單發(fā)消息支持針對(duì)regID和別名兩種方式,regID是小米推送服務(wù)后臺(tái)根據(jù)設(shè)備標(biāo)識(shí)+appID+時(shí)間戳生成,為了減少設(shè)備碰撞概率,設(shè)備標(biāo)識(shí)我們采用的依據(jù)是imei+AndroidID+build序列號(hào)。別名是App在客戶端設(shè)置上報(bào)的,便于應(yīng)用將自己的設(shè)備/用戶標(biāo)識(shí)符同我們的regID作關(guān)聯(lián),這樣App就不需要在后臺(tái)維護(hù)regID跟設(shè)備/用戶的對(duì)應(yīng)關(guān)系了。群發(fā)消息采用打標(biāo)簽的方式來區(qū)分,客戶端和服務(wù)端都可以給指定設(shè)備設(shè)置標(biāo)簽,發(fā)消息的時(shí)候,只需選取指定標(biāo)簽發(fā)送即可,小米推送后臺(tái)會(huì)將標(biāo)簽所對(duì)應(yīng)的設(shè)備展開。一個(gè)標(biāo)簽支持的設(shè)備數(shù)無上限。
那小米推送服務(wù)的穩(wěn)定性是如何保證的呢?小米推送服務(wù)采用多機(jī)房方案,平時(shí)流量均攤,一旦某個(gè)機(jī)房出現(xiàn)故障,流量無縫切換到其它機(jī)房,并且單個(gè)機(jī)房的容量能保證提供無損服務(wù)。目前是雙機(jī)房部署,預(yù)計(jì)明年會(huì)擴(kuò)展第三個(gè)機(jī)房。
安全性也是小米推送服務(wù)重點(diǎn)考慮的一個(gè)因素。數(shù)據(jù)傳輸過程中,得益于推送服務(wù)采用的雙層協(xié)議方案,消息會(huì)采取雙重加密,***重是XMPP傳輸層,保證數(shù)據(jù)在網(wǎng)絡(luò)傳輸?shù)倪^程中不會(huì)被篡改、監(jiān)聽。第二重是在Thrift二進(jìn)制層,用以保證消息到達(dá)Service之后,通過broadcast發(fā)送給App進(jìn)程的過程中不會(huì)被截獲和偽造。第二重加密往往會(huì)被其它第三方推送服務(wù)忽略,但其風(fēng)險(xiǎn)同樣很大。
性能指標(biāo)
11.11大促,所面對(duì)的請(qǐng)求量是在小米推送服務(wù)的設(shè)計(jì)容量之內(nèi)的,目前設(shè)計(jì)和機(jī)器規(guī)??梢灾С址逯得糠昼?000萬條消息;平時(shí)業(yè)務(wù)量至少每分鐘40萬,峰值每分鐘600萬條消息。
推送消息量平時(shí)波動(dòng)很大,所以開發(fā)團(tuán)隊(duì)準(zhǔn)備著流量隨時(shí)可能忽增200%的情況,并在線下做好壓力測(cè)試和優(yōu)化;如果流量特別大,還有以下應(yīng)對(duì)措施:
- 異步排隊(duì)處理,此時(shí)消息送達(dá)時(shí)間可能會(huì)比平時(shí)稍慢,但不會(huì)對(duì)整個(gè)系統(tǒng)有太大沖擊;
- 消息有優(yōu)先級(jí),廣播消息會(huì)以低優(yōu)先級(jí)處理;
- 限流,控制開發(fā)者發(fā)送消息的頻率;
- 擴(kuò)容,如果機(jī)器負(fù)載過高或者某個(gè)服務(wù)有瓶頸,可以很快速地增加機(jī)器,部署服務(wù),增強(qiáng)系統(tǒng)處理能力。
小米推送服務(wù)所經(jīng)歷的重構(gòu)
軟件系統(tǒng)在開發(fā)和演進(jìn)過程中,經(jīng)常會(huì)經(jīng)歷較大規(guī)模的重構(gòu)。小米推送服務(wù)有兩次比較大的重構(gòu)。
一是開發(fā)語言從Erlang 轉(zhuǎn)為Java。 小米原來的消息系統(tǒng)是使用Erlang開發(fā)的,所以推送系統(tǒng)的***版也是基于Erlang;但是Erlang的社區(qū)不夠活躍,開發(fā)人員很難找,學(xué)習(xí)曲線陡,支持工具和類庫少,所以后來開發(fā)團(tuán)隊(duì)選擇了使用Java重新開發(fā);遷移到Java后,對(duì)開發(fā)人員的要求降低,各種工具和類庫較多,大大提高了開發(fā)效率。
二是無處不在的Cache??蛻舳耸褂眯∶淄扑头?wù)的SDK,開發(fā)者使用API的情況千變?nèi)f化,很多場(chǎng)景是意料之外的;需要對(duì)調(diào)用頻繁的業(yè)務(wù)添加Cache,盡可能在本地進(jìn)程內(nèi)處理;例如,對(duì)于客戶端調(diào)用API設(shè)置別名和訂閱topic,先檢查Cache是否已經(jīng)設(shè)置過,只有沒有設(shè)置才往后端服務(wù)發(fā)送;優(yōu)化后,后臺(tái)服務(wù)的業(yè)務(wù)壓力大大減少。
在開發(fā)小米推送過程中的一些感悟
-
服務(wù)要支持水平擴(kuò)展,盡可能實(shí)現(xiàn)為無狀態(tài),或者使用一致性哈希進(jìn)行劃分;方便擴(kuò)容,可以保證即使系統(tǒng)暫時(shí)有性能瓶頸也能通過加機(jī)器解決。
- 監(jiān)控先行,能夠很方便地采集、分析服務(wù)器的負(fù)載和業(yè)務(wù)的請(qǐng)求量、percentile、slow log,能夠清楚了解到系統(tǒng)的瓶頸,有針對(duì)性地改進(jìn)。
- 不要過早優(yōu)化,先實(shí)現(xiàn)功能并盡快上線,根據(jù)監(jiān)控?cái)?shù)據(jù)對(duì)關(guān)鍵地方進(jìn)行優(yōu)化。
- 敏捷開發(fā),快速迭代,日拱一卒,每天都有簡(jiǎn)短的站立會(huì)議,能夠迅速響應(yīng)變化,持續(xù)改進(jìn)系統(tǒng)。