100 億次的挑戰:如何實現一個 “有把握” 的春晚搖一搖系統
這是2015年年初(羊年春晚)搖一搖活動的技術復盤文章。在兩年之后再次溫習,有很多架構上的取舍仍然可以給類似的活動參考,因此重新發出來。
羊年春晚搖一搖活動已經落下帷幕,現在回過頭來看看這一全民參與的有趣的活動背后,有著怎樣的后臺系統?這個系統又是如何被設計與實現出來的?
春晚搖一搖活動
在了解這個系統之前,先看看羊年春晚有哪些活動形式?春晚搖一搖復用了搖一搖入口,但提供了全新的界面和交互內容。
在羊年春晚搖一搖界面里,用戶搖動手機后,可以看到明星拜年、全家福、好友賀卡等精彩紛呈的活動頁;也會有溫馨的“休息一下”,或讓很多誤以為中獎的“掛服務器”等特殊用途的頁面。
大家最期待的肯定是搖紅包,搖中紅包的幸運用戶除了自己領到一份紅包(種子紅包)外,還可以領到若干份用于分享給其他好友的紅包(分裂紅包)。
圍繞這些活動,下面將會通過4個處于項目不同階段的里程碑版本來介紹我們設計、實現這一系統過程中的一些思考和做法,特別是標題里提到的“有把握”是由何而來。
V0.1原型系統
原型系統很簡單,但已經基本實現了春晚搖一搖的需求。原型系統的架構見下圖。
相關的處理流程如下:
-
用戶搖動手機后,客戶端產生搖一搖請求,請求發到接入服務后,會被轉發到搖一搖服務;
-
搖一搖服務會根據現場節目的流程,經過一系列的邏輯判斷,給客戶端返回一個結果:明星拜年、紅包或者其他活動;
-
假設是搖到了紅包,由于紅包都是企業贊助的,需要對企業形象進行展示,客戶端會從CDN拉回這個企業的LOGO等資源,最終展示出一個完整的紅包;
-
隨后用戶拆紅包時,請求會進入紅包系統,再到支付系統,最后到財付通系統完成一系列復雜的賬務處理,最終拿到紅包;
-
用戶還可以分享紅包,被分享的紅包通過消息系統發給好友或群,其他人可以再搶一輪;
-
在這一過程中,安全系統保障紅包活動的業務安全。
上述數據的流動可以分下類:資源流、信息流、業務流和資金流。本文將主要聚焦在資源流和信息流。
面臨的挑戰
原型系統看起來已經足夠簡單,功能也基本完備,是不是可以稍加修改后直接用在春晚現場呢?答案肯定是不行。那么問題來了:為什么不行?
回答這一問題前,我們先看一下看似簡單的活動背后,面臨著哪些挑戰?
海量用戶請求,預計請求峰值1000萬/秒
1000萬/秒究竟是多大的規模,可以通過下圖更直觀地感受下:
注:搶火車票數據引用自公開數據
春晚全程互動,不確定因素多
這個系統需要跟羊年春晚全程緊密互動,從項目開始到結束,有一系列的不確定因素會加大系統實現的復雜度:在開發階段,針對節目與活動形式如何配合這個問題的討論有可能持續到春晚前,如何使系統能服務多變的需求?在春晚現場,節目數量多,節目時長甚至順序都有可能調整,如何做到現場節目與搖一搖活動無縫銜接?
系統深度定制,成敗在此一舉
作為專為春晚設計的系統,部署上線后真正能運行的時間就只有幾個小時,這幾個小時內,常規系統所提倡的灰度發布、先扛住再優化等做法并不是太適用。在這短暫的時間內,只有一次機會:要么成功,要么失敗。
全民高度關注,必須成功
春晚會有7億左右的觀眾,大家對這一活動抱有很大期望,全民矚目之下,只能成功,不能失敗。
缺少歷史經驗,把握不大
如此大型的活動,對我們而言是史無前例的,并沒有太大的信心。前邊提到的1000萬/秒的峰值是如何估算出來?各個環節會有多少用戶參與?系統需要預留多少資源?這些問題不會有現成的答案,都需要摸索與思考。
可見,在看似簡單的活動背后,隱藏了巨大的挑戰,之前假設的原型系統不太可能勝任,需要做更深入的優化。
需要優化哪些環節?比較顯而易見的有三個:
流量帶寬
春晚搖一搖需要用到大量的多媒體資源,這些資源都需要從CDN下載。經過評估,帶寬峰值需求是3Tb/s,會帶來巨大的帶寬壓力。即使我們有無限的資源,帶寬需求能被滿足,客戶端在搖一搖后下載資源所需的等待時間也會帶來很大的用戶體驗損害,是不可接受的。
接入質量
接入是后臺系統的第一道關,所有請求都會到達接入。預計當晚會有3.5億的在線,如何盡可能保障外網接入質量?甚至在外網波動時也不受太大影響?
海量請求
1000萬/秒的請求從外網涌進來后,都被路由給搖一搖服務,也就是說搖一搖服務也將有1000萬/秒的請求量。這意味著需要確保系統內2個1000萬/秒的高可用,在分布式系統內,這是個挺大的問題。
如果要對系統能否最終取得成功量化一個信心指數的話,這個原型系統的信心指數是10。這個數字表示如果春晚搖一搖采用這套系統并取得成功,我們認為90%是靠運氣。也可以換種說法:拿這個系統到春晚搖一搖,90%的可能會掛掉。
系統肯定不能以運氣為基礎,這幾個問題如何解決?
V0.5 測試版
V0.5的目標就是為了解決V0.1原型系統里的幾個問題。
資源預下載
春晚使用的資源比較多,也比較大,但基本都是靜態資源,是可以提前幾天下載到客戶端的。保存到本地后,在需要使用的時候,客戶端直接從本地加載,從而省去了集中在線下載所需的帶寬需求,另外用戶搖一搖時不再需要下載資源,可以有更好的用戶體驗。下圖展示了資源預下載過程。
資源推送模塊負責將資源上傳到CDN,同時推送資源列表給客戶端。推送過程基于微信消息系統實現,可以在極短時間內把資源列表推送給幾億的用戶。
資源預下載需要解決以下幾個問題:
-
資源交付情況
-
資源更新
-
資源下載失敗
-
資源覆蓋率
-
離線資源安全
通過這套資源預下載系統,2015.2.9 - 2015.2.18 期間,下發了資源65個,累計流量3.7PB,其中閑時峰值達到1Tb/s。
外網接入梳理
要保證接入質量,除了需要保證接入服務本身的穩定性外,需要做到:
-
具備在某些外網接入出現波動的時候,能自動切換到正常接入服務的能力;
-
保障網絡與服務具備足夠的冗余容量。
微信客戶端已經具備了外網自動容災切換的能力,下邊再看一下系統的外網部署情況。
我們重新梳理了外網的部署,最終在上海IDC和深圳IDC各設置了9個TGW接入集群。每個IDC都在3個不同的園區分別部署了電信、移動和聯通的外網接入線路。
另外,接入服務也進行了臨時擴充,兩地共有638臺接入服務器,最多支持14.6億的同時在線。
接入服務內置“搖一搖”
架構變動
前面提到,系統需要面對預計1000萬/秒從外網的請求,同時還需要在內網轉發同樣是1000萬/秒的請求給搖一搖服務,除此之外,還需要在各種異常情況下確保高可用。在如此海量請求之下,在這個分布式系統中,任何一點網絡或服務的波動都可能是災難性的,這里邊的困難程度可想而知。
正面解決這一問題的成本過高,我們選擇了去掉搖一搖服務——把搖一搖邏輯集成到了接入服務,從而去掉了1000萬/秒的轉發請求。
但這樣做,有一個前提:不能降低接入服務的穩定性。因為不單是春晚搖一搖請求,微信消息收發等基礎功能的請求也需要通過接入服務來中轉,假如嵌入的搖一搖邏輯把接入服務拖垮,將得不償失。
接入服務的架構剛好有助于解決這個問題。
如上圖所示,接入服務有一個網絡IO模塊,這個模塊維護了來自客戶端的TCP連接,收發數據,負責跟客戶端通訊。網絡IO模塊是作為獨立的進程組運作的,收到的請求通過本機共享內存發給接入邏輯模塊處理。接入邏輯模塊也是一組獨立運行的進程,通常情況下是由請求中轉邏輯通過RPC把請求轉發給其他邏輯服務器處理。現在搖一搖邏輯作為嵌入邏輯,整合到了接入邏輯模塊。由于網絡IO模塊與接入邏輯模塊相互隔離,可以單獨對接入邏輯模塊進行升級,不會影響到已有的網絡連接,這大大降低了嵌入邏輯帶來的風險。
但這樣還不夠,還可以把嵌入到接入邏輯模塊里的搖一搖邏輯盡可能簡化,只保留比較簡單、穩定、只需要單機計算即可完成的輕量邏輯;把其他較復雜、可能需要經常變更、或需要跨機訪問的邏輯獨立為搖一搖agent,作為獨立運行的進程,通過本機共享內存與嵌入搖一搖邏輯通訊。
搖一搖邏輯實現
接入服務架構的修改使得內置搖一搖邏輯變得可能,剩下的就是怎樣實現春晚搖一搖邏輯?春晚搖一搖邏輯最重要的是搖紅包,搖紅包則需要重點解決以下問題:
紅包如何發放?
所有紅包都源自紅包系統,但為了在運行時不依賴紅包系統,紅包系統生成的種子紅包文件被提前部署到了接入服務。為確保紅包文件不會被重復發放,有個文件切分程序完成不同機器的文件切分,每臺機只保留自己需要處理的那部分紅包;另外,為了確保紅包不漏,還有另一個程序完成所有機器紅包文件的合并校驗。
搖到紅包的用戶如果選擇不分享紅包,這些紅包會被紅包系統回收,一部分作為種子紅包經由搖一搖agent回流到接入服務進行發放。
搖一搖agent會根據預設的策略給內置的搖一搖邏輯提供可下發的種子紅包。紅包是每秒勻速下發的,可以精確控制全局紅包下發速度,保持在紅包/支付系統能正常處理的范圍之內。
怎樣保障安全?
-
單用戶紅包個數限制
業務要求每個用戶最多可以領取 3 個紅包,并且每個贊助商最多 1 個。常規實現里,需要在后臺存儲用戶領取記錄,每次搖一搖請求時取出來計算,如果拿到紅包還需要更新存儲。但這里增加了對存儲的依賴,海量請求下,穩定性不易保障。我們借鑒了 HTTP 協議的 COOKIE ,在搖一搖協議里也實現了類似的機制:后臺可以寫入用戶的紅包領取情況到 COOKIE ,交由客戶端保存,客戶端在下次請求時帶上來,服務器再取出來判斷。這就無需后臺存儲也可以實現同樣的目的。
-
破解協議的惡意用戶
協議總是有可能被破解的,惡意用戶可以制作自動機,繞開 COOKIE 檢測機制。所以 COOKIE 檢測機制只能用于絕大部分使用正常客戶端的用戶。對于破解協議的惡意用戶可以通過在本機存儲紅包下發記錄來打擊。
-
游擊戰
雖然接入服務使用長連接,但惡意用戶可以不斷重連,變換不同的服務器來獲取紅包,針對這種情況,我們設計了紅包發放匯總服務,可以在所有接入服務上同步已經達到紅包領取上限的用戶。
以上策略依次遞進,后一步較前一步更深入一層解決問題,但是在設計上,前一步并不依賴后一步,即使后邊的步驟出現故障無法按預期進行,也不會影響前邊的結果,在最差的情況下,系統仍然可以保證單用戶紅包個數限制的需求。
如何與春晚現場同步互動?
春晚搖一搖系統需要配合春晚現場節目進行互動,春晚切換節目或者主持人進行活動的口播時,系統都需要同步切換活動配置。
這里一是要求快速,二是要求穩定。為此我們設計了這樣一個配置系統。
春晚現場有一個配置前臺,現場人員可以通過這個配置前臺發送變更指令到后臺。后臺有上海和深圳兩個接收點,每個接收點由處于不同園區的3組冗余的配置服務構成。配置服務接收到配置后會同步到另一接收點,同時以文件形式下發到所有接入服務。為確保同步成功,使用了RPC/RSYNC/變更系統三個冗余變更通道來同時變更配置。從配置前臺發起變更,到接入服務加載配置,全程可以在10秒內完成。
看起來快速和穩定都滿足了,是不是就已經足夠了?請思考以下異常場景:
-
春晚前臺無法工作或網絡故障,指令無法發給配置服務;
-
所有配置服務均無法工作或網絡故障,配置無法同步給接入服務。
這些異常在春晚節目過程中臨時出現的話,一般情況下影響并不大,我們可以臨時手工變更配置,贏得足夠時間去修復系統。但是,如果此時主持人正在號召大家拿起手機搶紅包,我們是沒有足夠的把握可以在極短時間內完成全系統的配置變更的,也就是說極有可能功虧一簣,關鍵配置變更不了,紅包根本出不來。還有一種類似的場景,就是搶紅包開始后,也因為配置變更問題,結束不了,沒法切回其他活動,損壞用戶體驗。
針對口播搶紅包這個關鍵點,我們采用這樣的配置變更策略:使用搶紅包倒計時配置,到點后自動開始搶紅包時段,經過預設的時間后,又自動結束。另外,由于春晚節目的口播時間點具有不確定性,我們制定了變更策略,在節目過程中可以逐步校正倒計時。于是這一關鍵配置就只與時間相關,機器時間相對容易保障,通過NTP就可以達到足夠精度的時間同步,我們還對機器時間進行集中監控,可以發現并處理機器時間出現異常的機器。
預估帶來的問題
至此, V0.1 原型系統前邊提到的三個問題均已解決, 1000 萬 / 秒的海量請求可以扛住。但是, 1000 萬 / 秒是預估的,如果最終來了 2000 萬怎么辦? 4000 萬呢?看起來數字有點不可思議,但真有 1 億甚至幾億用戶在不停的搖,也還是有可能出現的。
對于2000萬/秒,我們其實并不擔心,接入服務經過不斷的優化,可以在提供容災冗余的基礎上,仍有2500萬/秒的吞吐能力。
但是4000萬/秒大大超出了后臺服務能力,怎么辦?
海量服務之道有個過載保護,如果沒法硬扛,可以做到自我保護。簡單的說就是前端保護后端,后端拒絕前端。
客戶端在服務訪問不了、服務訪問超時和服務限速時主動減少請求。
接入服務可以在發現某個客戶端請求過于頻繁時,會自動延遲回包,間接達到拉大請求間隔的效果,最終實現在后臺把客戶端請求頻率限制在合理范圍內;另外,接入服務會計算機器的CPU使用情況,CPU使用率到達不同預設閾值時,會自動返回不同檔次的限速給客戶端,通過客戶端配合完成限速。
通過這些措施,在請求量超預期時,系統會自動降級,從而實現自保。
綜上,可以得到V0.5測試版的架構,信心指數50。
V0.8 預覽版
核心體驗是什么?
為什么V0.5測試版的信心指數只有50?還有哪些沒做到位?
反思一下,可以發現:V0.5測試版主要聚焦于如何處理海量的搖一搖請求,讓用戶可以愉快的搖出紅包,那搖出紅包之后呢?V0.5測試版并未對這個問題作進一步的思考。
先看一下搖一搖的核心體驗——搖紅包,搖紅包涉及搖到紅包、拆紅包、分享紅包、好友搶紅包等等步驟。
稍加分析即可發現,前三步是本人操作,后續是好友操作。從本人操作到好友操作之間是有一定時延的,這也就意味著這里其實可以接受一定程度的有損體驗——延時。
V0.5測試版已經解決搖紅包的問題,剩下就看拆紅包和分享紅包了。
如何確保拆紅包和分享紅包的用戶體驗?
可以把拆紅包和分享紅包抽象一下,它們都是由兩個部分組成:用戶操作(信息流)和后臺的紅包處理邏輯(業務流)。對用戶來說,他關心的是操作是否成功,而不是后臺的紅包處理邏輯。因此這個問題可以進一步簡化為:如何確保拆紅包和分享紅包的用戶操作能成功?
我們在用戶操作和紅包處理邏輯之間加入了一個中間層。這個中間層包括紅包簡化邏輯和紅包異步隊列,實現了信息流和業務流的異步化和解耦。
紅包簡化邏輯
通過一些算法設計,紅包簡化邏輯可以通過本地計算完成對紅包請求是否合法的初步判斷,合法請求放入異步隊列,然后就可以返回客戶端處理成功;
紅包異步隊列
紅包異步隊列存儲來自簡化邏輯的請求。在存儲上,異步隊列使用3機方案,3機寫2份成功即算入隊成功。異步隊列的處理邏輯會從隊列取出請求繼續發起對紅包系統的調用,完成后續的紅包邏輯。在紅包系統故障時,會有妥善的重試策略,確保在系統恢復后重試成功。
接入服務、紅包簡化邏輯服務、紅包異步隊列構成了所謂的“鐵三角”,可以在紅包系統出現嚴重問題時,保護用戶體驗在較長時間內基本不受損,爭取到修復故障的寶貴時間。
其實再仔細思考一下,“鐵三角”還可以做得更強:把紅包簡化邏輯再次拆分為基本的業務邏輯和本地透傳隊列。業務邏輯完成紅包合法性校驗后,把請求寫入本地文件隊列,本地文件隊列再透傳給紅包異步隊列完成后續處理。這樣即使紅包異步隊列出現意外抖動,也不會影響到用戶操作結果。
V0.8預覽版已經基本成型,信心指數達到70。
V1.0 正式版
大家知道設計≠實現,為保證最終實現和線上系統運作符合設計預期,我們還做了:
全程壓測
未經壓測的系統是很危險的,無法真正了解系統在壓力下的表現是否符合預期?系統的極限在哪?為此,我們建立一個壓測環境,對系統進行不間斷的壓測。系統部署后,在現網環境也進行了多輪真實壓測。
專題CODE REVIEW
多部門聯合做了專題 CODE REVIEW ,在代碼層面對關鍵路徑做了仔細的評估。
內部演練
客戶端發布流程較長,全面鋪開版本需要更長的時間,做不到像服務器一樣可以迅速上線 fix 問題。因此在發布客戶端前,除了測試外,一次次的真實演練是非常必要的。
線上預熱
在2015.2.12和2015.2.15,最終爭取到了兩次預熱的機會,對系統進行了實戰驗證。
2015.2.12 |
2015.2.15 |
|
搖一搖總次數 ( 次 ) |
3.1億 |
4000萬 |
搖一搖峰值 ( 次 / 分 ) |
5000萬 |
1700萬 |
搖一搖峰值 ( 次 / 秒 ) |
100萬 |
43萬 |
發放紅包速率 ( 個 / 秒 ) |
5萬 |
5萬 |
因為預熱規模的原因,搖一搖峰值并未達到預估值。但發放紅包的速率一直維持在5萬/秒的預設值,對后端“鐵三角”和其后的紅包系統進行了充分的檢驗。
復盤與調整
每次演練和預熱都是難得的機會,不僅那些出異常的模塊,那些看起來表現正常的模塊,也可以通過復盤和推演,確認是否完全符合預期,挖掘出可能的隱患點,進而解決。
最終,我們發布了 V1.0 正式版。
V1.0正式版架構上與V0.8預覽版并無不同,但是信心指數加到了80。
我們認為剩下的20%里,有10%體現在對突發狀況的預案與現場處置。系統如此龐大與復雜,是不是還有沒想到、未暴露出來、還做得不夠或者在有限資源內無法解決的問題?這些問題一旦如果出現了,是不是有可能最終左右了整個局面?所以最后的10%就是靠運氣,別出現這些問題。
后記
來自:http://mp.weixin.qq.com/s/DLbaiSiH15QBkrB_i0imEw