春節的三波紅包雨——搖紅包已經過去一段時間,面對億級的企業資金以及億級的紅包,回想這其中的過程并不簡單,充滿各種變數,稍有閃失就可能功虧一簣。讓我們一起回顧一下這里的準備過程,看看技術上是如何為這只許成功不許失敗的項目保駕護航的。
一、除夕活動紅包系統
紅包系統由三部分組成:信息流、業務流和資金流。這三部分在組織架構上由不同的后臺團隊完成:信息流——微信后臺,業務流——微信支付后臺,資金流——財付通后臺。
在平時,紅包系統主要處理個人會話中以消息形式發出的紅包。其中,信息流主要包括用戶操作背后的請求通信和紅包消息在不同用戶和群中的流轉;業務流是用戶請求引發的包紅包、搶紅包和拆紅包等的業務邏輯;資金流則是紅包背后的資金轉賬和入賬等流程。
紅包系統在除夕活動時和平時的實現不大一樣。在除夕活動時,除了個人紅包外,紅包系統還要處理由后臺通過搖一搖集中下發的大量企業紅包。這里邊信息流的實現變化較大。
接下來簡單介紹一下 2016年 除夕活動時的紅包系統架構,包括三個方面:資源預下載、搖紅包、拆紅包。?

1 資源預下載
在除夕,用戶通過搖一搖參與活動,可以搖到紅包或其他活動頁,這些頁面需要用到很多圖片、視頻或 H5 頁面等資源。在活動期間,參與用戶多,對資源的請求量很大,如果都通過實時在線訪問,服務器的網絡帶寬會面臨巨大壓力,基本無法支撐;另外,資源的尺寸比較大,下載到手機需要較長時間,用戶體驗也會很差。因此,我們采用預先下載的方式,在活動開始前幾天把資源推送給客戶端,客戶端在需要使用時直接從本地加載。
2 搖 / 拆紅包
除夕的搖一搖子系統是專門為活動定制的,按時間軸進行各項活動,這里邊最重要、同時也是請求量最大的是搖紅包。從需求上看,系統需要完成兩個事:用戶可以通過搖一搖搶到紅包,紅包金額可以入到用戶的支付賬戶。在除夕,系統需要在很短時間內將幾十億個紅包發放下去,對性能和可用性要求很高。
考慮到涉及資金的業務邏輯比較復雜,還有很多數據庫事務處理,耗時會比較長,于是我們將搶紅包(信息流)和紅包的賬務邏輯(業務流和資金流)異步化。將前一部分處理流程盡可能設計得輕量,讓用戶可以很快搶到紅包,然后再異步完成剩下的賬務邏輯。
那么,搶紅包階段是怎樣做到既輕量又可靠呢?
1)零 RPC 調用?
在微信后臺系統中,一般情況下客戶端發起的請求都是通過接入服務轉發給具體的業務服務處理的,會產生 RPC 調用。但對于搖一搖請求,我們將搖一搖邏輯直接嵌入接入服務中,接入服務可以直接處理搖一搖請求,派發紅包。
2)零數據庫存儲?
按一般的系統實現,用戶看到的紅包在系統中是數據庫中的數據記錄,搶紅包就是找出可用的紅包記錄,將該記錄標識為屬于某個用戶。在這種實現里,數據庫是系統的瓶頸和主要成本開銷。我們在這一過程完全不使用數據庫,可以達到幾個數量級的性能提升,同時可靠性有了更好的保障。?
支付系統將所有需要下發的紅包生成紅包票據文件;?
將紅包票據文件拆分后放到每一個接入服務實例中;?
接收到客戶端發起搖一搖請求后,接入服務里的搖一搖邏輯拿出一個紅包票據,在本地生成一個跟用戶綁定的加密票據,下發給客戶端;?
客戶端拿加密票據到后臺拆紅包,后臺的紅包簡化服務通過本地計算即可驗證紅包,完成搶紅包過程。
3)異步化?
用戶搶到紅包后不會同步進行后續的賬務處理,請求會被放入紅包異步隊列,再通過異步隊列轉給微信支付后臺,由微信支付后臺完成后續業務邏輯。
二、大規模集群中保證數據一致性
事實上網絡分裂很難從根本上避免,我們在設計系統時都是假設網絡分裂一定會出現,基于此思考應該采用什么方案保障系統在網絡分裂時能正常運作。
我們的方案是在每個數據中心都建設三個獨立的數據園區,可以做到在任意一個數據園區出現網絡分裂等故障,甚至徹底變成園區孤島后,另外兩個數據園區可以無損承接整個數據中心的請求。
三園區容災的關鍵就是數據一致性。我們需要做到在分裂出去的那個數據園區的數據在其他園區有個強一致的副本,這樣請求落到其他兩個園區后,可以無損完成服務,此外在故障園區恢復后,數據在所有園區還能自動保持強一致。微信后臺實現了基于 Quorum 算法,對數據有強一致性保證的存儲系統——KvSvr(這一存儲系統另文介紹)。
此外還有可以提供三園區強一致保證的可靠異步隊列,這次就應用在這個紅包系統中。前邊提到的部署在接入服務的紅包文件實際上也是可以實現三園區容災的,我們在每臺接入服務部署的紅包文件都會在其他數據園區有個備份。在某個數據園區故障時,我們可以在其他數據園區發放故障園區的紅包。
今年活動紅包總量非常大,活動形式也更豐富,我們在以下方面做了優化。
1 服務性能
為提升各個服務模塊的處理性能,我們通過壓測和 Profiler 分析,發現了不少性能瓶頸點,做了大量優化。
2 業務支撐能力
支持更加復雜的業務場景,并在客戶端和服務器都加入了很多可以后期靈活調整的預埋能力,以更好地服務產品運營。
3 可用性
不斷提升系統可用性是我們一直努力的目標,以下 5 點很好地提高了系統的可用性。
1)系統容量評估與配額?
對系統的容量需要有個準確的評估與驗證,并結合業務設計合理的配額方案和降級方案,盡可能保障系統不會過載。例如,我們評估并驗證完系統每秒最大拆紅包量后,就可以在處理用戶搖一搖請求時,限制系統每秒最大發放紅包配額,這就間接保證了拆紅包量不會超出處理能力。
2)過載保護?
服務如果出現過載了,必須有能力自保,不被壓垮,并且不擴散到系統其他的服務。我們在后臺的服務框架層面具備通用的過載保護能力:服務如果處理不過來,就按請求的優先級盡快丟掉超出處理能力的請求,保證服務的有效輸出;上游調用端在部分服務實例過載時,能自動做負載均衡調整,將請求調整到負載較低的服務實例中;上游調用端發現大部分服務實例都出現過載,也可以主動丟掉部分請求,減輕后端服務器的負擔。
3)減少關鍵路徑?
減少核心用戶體驗所涉及的步驟和模塊,集中力量保證關鍵路徑的可用性,從而在整體上提高可用性。我們把活動紅包的信息流和業務流進行異步化,就是基于這個考慮。跟用戶核心體驗相關的搶紅包操作,在信息流中的接入服務、紅包簡化邏輯服務和紅包異步隊列(入隊)這三個服務模塊參與下即可完成。這三個服務模塊是可以比較容易用較低成本就做到高可用的,可以較好地規避業務流和資金流中幾十甚至上百個服務模塊可能出現的風險。
4)監控指標?
我們需要對系統的真實負載情況有準確及時的了解,就必須要有一套高效、可靠的監控系統,同時還要有一套有效的監控指標,監控指標不是越多越好,太多了反而會影響判斷,必須要有能準確反映問題的幾個核心指標。在我們系統里,這些核心指標一般在基礎框架集成,根據經驗來看,其中一個很有用的指標是服務的最終系統失敗。
我們把服務的失敗分為兩類:邏輯失敗和系統失敗。系統失敗一般是服務暫時不可用導致,是可通過重試來自動解決的,如果請求重試若干次仍然為系統失敗,就產生最終系統失敗。通過最終系統失敗通常可以快速定位到異常的服務,及時進行處置。
5)人工介入?
我們在紅包系統內預置了很多配置開關,當自動運作的過載保護無法發揮預期作用時,可以通過人工介入,使用這些保底的手動開關迅速降低負載、恢復服務。
三、技術創新
實際上,類似的這種活動用到的技術都是現成的,并不復雜。但為什么大家會覺得很難實現呢?主要是因為規模:用戶和請求的并發規模越大,就越難在系統的成本和可用性上達到平衡,也就是說越難實現一個低運營成本、高服務可用性的系統。
在傳統的應對這種有很大規模用戶參與的活動的實現方案里,一般會采用在客戶端過濾請求,以某種概率(基于時間或互動次數)發到服務器進行處理,這樣服務器的壓力可以大幅降低。
我們認為還可以做得更好,在這種活動的技術方案上可以有所突破——在保持低成本的前提下,全量處理用戶的每次交互。這就大大降低了客戶端的實現風險(因為客戶端的更新和覆蓋周期相對較長)。此外,服務器有了對用戶交互有了全面的控制能力和靈活調整的能力。
這些能力對活動的運營是非常可貴的。可以讓我們在活動過程中,各種復雜用戶場景下,都能做到精細的調整,給了產品運營很大的靈活性,以保證活動效果和用戶體驗。看看下面兩個例子。?
?我們可以精確控制和調整每次用戶交互出現什么結果,曝光哪個贊助商;?
活動過程中,有個很難預估的因素——參與人數。說不準參與的用戶少了(或互動次數少了),導致紅包很久都發不完?或者參與的用戶多了(或互動次數多了),導致需要加快發放速度,更快速發完紅包?
于是我們對這個技術方案做了全面的思考和設計,最終實現了現在這個系統,可以用很低的成本實現極高的性能和可用性,在除夕活動中得到了成功的應用。
四、服務降級方案
我們對搖一搖 / 朋友圈紅包照片在 2016.1.26 的預熱活動和 2016.2.7 的正式活動都做了詳細的復盤。包括活動過程中各項業務數據是否符合預期,各個模塊的表現是否符合預期等,并分析各種不符合預期表現的成因和解決措施。
在紅包系統的信息流、業務流和資金流都有很多保障用戶核心體驗的降級方案。舉幾個信息流的降級方案的例子。
a) 如果某一個數據園區出現網絡分裂等故障,完全不可用了,部署在那里的紅包怎么發下去??
紅包文件雖然在園區間有冗余存儲,但基于性能和可用性考慮,我們并不打算在各園區間維護強一致的紅包發放記錄,做到記錄級的 “斷點續發”,而是將紅包文件按時段進行切分,降級為只做文件級的 “斷點續發”。在某個園區不可用時,使用降級方案后,故障園區當前發放時段的紅包文件不會接著發放,僅保證下一時段的紅包能通過其他園區正常發出去。
b)活動過程中,如果用戶的交互量超過服務的處理能力了怎么辦??
正如前面所述,我們很難準確估計參與用戶量及互動次數。就本次活動而言,在系統設計之初,我們評估有 2000 萬次 / 秒的峰值請求,并在系統最終實現和部署時預留了一定的余量,提供了評估值 2.5 倍(也就是 5000 萬次 / 秒峰值請求)的系統處理能力,除夕當晚服務器處理請求峰值達到 2500 萬次 / 秒,服務實際負載低于 50%。但如果當時用戶過多,互動過于火爆,到達后臺的請求超過 5000 萬次 / 秒,系統就會進入降級模式,客戶端可以在服務器的控制下,減少請求,防止服務器過載。
c)紅包發放速度過快,后端處理不過來怎么辦??
正如前面所述,用戶搶紅包是在信息流完成的,完成后請求放到紅包異步隊列再轉到業務流。如果領取紅包請求量過大,隊列會出現積壓,紅包的入賬會出現延時,但不會導致用戶請求失敗。
類似的降級方案還有很多,每個環節都有若干個不同的降級方案,有些是針對業務專門設計的(如 a 和 b),還有些是使用基礎組件 / 服務 / 方案本身就具備的降級能力(如 c)。這些方案都遵循著一個原則:降級時,盡可能保證用戶的核心體驗。
五、資金與紅包準備的難點
除夕活動資金和紅包準備的難點總體來說有以下 4 點。
1)紅包的數量?
由于招商的不確定性,最終投入微信搖企業紅包的資金不可準確預估;?
不同的商戶其對品牌的曝光量的需求不同,有的要求多曝光,有的要求多關注,紅包金額或大或小,數量則或多或少;?
從準備到除夕當天,期間各種變化,活動方案變數很多。?
紅包的數量可能從幾億到幾百億變化,資金與紅包的準備需要能夠滿足需求的巨大變化。
2)資金的就位?
企業各行各業,營銷經費的審批流程不盡相同,資金最終支付到位時間前后差異很大,甚至有些在除夕前一周才會最終敲定支付到賬;?
某些企業可能在最后階段停止合作;?
某些企業可能在最后階段調整資金的使用方式。?
以上情況會導致資金的到位時間不完全可控,本著盡可能多的資金能夠投入到活動中的原則,希望可以盡可能壓縮活動的準備時間,讓更多的資金能夠上車。
3)資金預算的配置方案(資金劇本)?
按設想的活動方案,紅包會分為三大類:圖片紅包、視頻紅包、品牌 logo 紅包。活動方案較去年又有新的變化,特別是 logo 紅包的認知度和接受度不確定,logo 紅包過度會否導致未領完而浪費資金,不容易確定 logo 紅包的資金占比;?
除夕當天的活動劇本可能會反復調整優化,紅包時段的劃分可能修改,不同時段的資金投放量可能變化。?
大筆資金、大量的紅包、復雜的活動方案、善變的商戶要求,都可能反復調整,如果面對百億量級的紅包配置調整時,技術上如何實現縮短準備時間,支持方便的變化。
4)資金的安全?
如何防止紅包的金額被篡改;?
如何防止未被領取的紅包被入賬給用戶;?
如何防止紅包金額重復入賬給用戶;?
如何防止機器被攻破產生了不存在的紅包;?
如何防止紅包被不同用戶重復領取;?
如何在確保資金安全的情況下盡可能保證用戶的到賬體驗(最好是實時或準實時到賬)。?
技術上必須做萬無一失的準備,確保資金足夠安全,活動順利完成。
六、微信搖企業紅包全過程
如果在除夕當天搖的過程中按前邊提到的超級復雜的配置方案即時生成隨機紅包,這顯然是風險齊高邏輯奇復雜的。對待只許成功不許失敗的項目,主流程必須極簡高效,所以這里全部的資金和紅包數量都需要按方案規則提前切分和準備好。
將預生成好的紅包數據(預紅包數據)進行準確的部署,搖紅包的資金和紅包準備的整體流程方案有兩個選擇。?

-
方案一:預紅包數據提供部署給微信的接入機和寫入紅包 DB,搖紅包過程由紅包接入機控制紅包的發放,拆紅包時修改紅包 DB 中的紅包數據;?
-
方案二:預紅包數據只提供部署給微信接入機,搖紅包過程由紅包接入機控制紅包的發放,拆紅包直接 Insert 到紅包 DB。?
第二個方案減少一次 DB 操作,如果是百億量級的紅包數據,可以極大減少數據導入、對賬等活動準備時間,特別是方案需要變更時。
七.充足準備
1 對資金預算和資金劇本進行合理的建模
首先,面對如此大量的資金和復雜的資金劇本,如何準確高效地管理和控制邏輯呢?要杜絕傻大黑粗,我們需要一個優雅的解決方案——建模。?

對資金預算和資金劇本進行合理的建模,讓模型涵蓋、掌管、控制一切——讓工具基于模型進行自動化的處理和校驗,這里需要做的就是:?
落地該模型的設計,讓一切圍著模型轉;?
按規定的格式文件導入預算和配置,并進行數據和邏輯合理性校驗;?
一切利用工具進行處理,資金的支付、退款、紅包的隨機生成和多商戶隨機打散、預紅包文件分割、預紅包數據的校驗,減少人為過程導致的潛在失誤;?
優化紅包隨機算法和文件處理方法,將紅包隨機分割和多商戶隨機打散算法的 n^2 時間復雜度優化 n,壓測 30 億紅包的生成時間為 2~3 小時,極大縮減準備時間,增加方案調整的回旋時間,可以讓更多的資金上車。
其次,前邊提到方案有如此多的變數、調整、變更,如何支持?還是回歸模型,建模時就要考慮支持同樣的預算多套資金配置方案。?

同樣的資金預算,同時或依次生成多套預紅包數據文件,提前做多手準備,方便方案變更。
再次,如此大量的資金就意味著如此大量的誘惑,會不會出問題呢??
方案預紅包數據未提前落地 DB,導致拆紅包時缺少一次紅包數據有效性的檢驗;?
預紅包數據存放在微信接入機上,存在被攻陷獲取或篡改的可能;?
紅包數據在傳輸的過程中存在系統異常或惡意攻擊,導致數據錯誤特別是金額錯誤的可能;?
系統內部可能存在惡意人員直接調用拆紅包的接口寫入不存在的紅包。
墨菲定律要求我們必須重視以上安全隱患,解決方案就是加密——對預紅包數據進行加密,加密庫和解密庫獨立維護保證密鑰不被泄漏,通過工具生成預紅包數據時用密鑰進行加密,預紅包數據在部署存儲和傳輸的過程中保持加密狀態,僅在拆紅包的邏輯中編譯二進制緊密庫進行解密。
同時,雞蛋也不能放在一個籃子里,為了控制密鑰泄漏導致的影響,確保資金風險可控,整個預生成的紅包數據可能分別使用了幾百到幾千個密鑰進行加密,一個密鑰加密的紅包資金量在 20~30 萬。解密庫還需要能設置密鑰 ID 的白名單和黑名單,確保未被使用的密鑰不能被利用,確認泄漏的密鑰可以進行攔截。
2 極限壓縮
如果是百億個紅包,那么產生預紅包數據文件的大小不經過壓縮是非常恐怖的,傳輸部署幾百 GB 或幾 TB 的數據是很艱苦的,一旦發生調整變更會變得非常艱難,所以需要對數據進行極限的壓縮。?
-
對于支付單號、商戶號、紅包賬戶等信息,由工具導成配置文件,配置到拆紅包邏輯中,加密的紅包數據中僅用一個批次 ID 表達;?
-
拆分紅包 ID,部分分段同樣轉為 ID,解密庫解密后利用配置進行還原;?
-
加密部分(Ticket):紅包 ID、金額、批次 ID、密鑰 ID,壓縮到 16 字節;?
-
單條紅包記錄二進制表達,壓縮到 26 字節。
3 對賬
上面所有都做到就安全了么?真的就有人寫了一個不存在紅包進來會怎樣?是否還有其他未考慮到的潛在風險?所以我們需要一個兜底——對賬,把一切都要對清楚才放心。?
對賬后再入賬的時效在 30~60 分鐘,會造成不好的用戶體驗。為了提升用戶體驗,將大額的預紅包數據(占比 10%左右)導入 KV(高速緩存),拆紅包時進行即時校驗,即時進行轉賬入賬,未命中 KV 的紅包則等待對賬后異步完成入賬。?
資金配置與資金預算要總分對賬;?
紅包數據文件與資金劇本進行總分對賬;?
紅包數據進行全局去重驗證;?
紅包數據進行解密驗證和金額驗證;?
如果密鑰泄漏紅包金額等被篡改,兜底要進行紅包 DB 中已拆紅包數據與預紅包數據的對賬后,才能實際進行轉賬入賬。
沈陽App定制開發,請信賴唯思科技!
