1. 程式人生 > >QQ紅包技術方案全解密(一)

QQ紅包技術方案全解密(一)

前言

自2015年春節以來,QQ春節紅包經歷了企業紅包(2015年)、刷一刷紅包(2016年)和AR紅包(2017年)幾個階段,通過不斷創新玩法,活躍度節節攀升,成為春節一大玩點,給火紅的春節帶來一抹亮色。2017年除夕,AR紅包、刷一刷紅包再創新高,搶紅包使用者數達 3.42 億,共刷出紅包 37.77 億個。

那麼,QQ紅包的技術方案究竟是怎樣的?其整體架構如何?重要的系統是如何設計的?為了保證使用者的體驗,手Q終端做了哪些優化?今年的QQ紅包又做了哪些新的嘗試,遇到的問題是如何解決的呢?本文將從架構開始,到手Q終端優化,再到個性化紅包和AR新玩法,為大家全面解密QQ紅包技術方案。

一、QQ紅包整體架構及重要系統

QQ春節紅包以一個又一個的整點刷紅包活動貫穿年三十,在除夕夜達到頂峰,是典型的海量使用者秒殺場景,如何應對海量的使用者刷紅包洪流,保證刷得爽,紅包安全到賬,是QQ紅包設計要解決的關鍵技術難點。另外,紅包專案涉及手Q終端、手Q後臺、QQ錢包(財付通)系統、禮券系統、公眾號等諸多業務系統,流程長且多,各系統效能吞吐量差異很大,如何保證各系統形成一個有機整體,協調高效提供服務,也是難點之一。

下圖為簡化後QQ紅包的架構,包括接入層、抽獎系統、儲存系統、發貨系統、公眾號訊息通知和CDN資源等幾部分,請大家先有一個整體的認知,便於閱讀下文。

本文將重點講解接入層、抽獎系統和發貨系統。

1.接入層

接入層是紅包後臺服務的大門,負責抽獎請求預處理,確保有效的請求才透傳給後端服務。為保證自身高可用、高穩定,接入層還可實時控制手Q請求頻率,避免海量請求壓垮接入層,出現不可控局面。

在海量服務場景下,為避免網路開銷,方便後端服務使用cache提升效能,接入層採用了一致性Hash定址,保證同一個使用者的請求只會落在同一臺紅包抽獎邏輯機器處理。

2.抽獎系統

抽獎系統作為QQ紅包的核心繫統,在承接使用者抽獎請求,按設計合理的機率完成抽獎操作,將抽獎結果安全落地儲存,並順利發貨等過程中,起到了關鍵作用。面對海量抽獎請求,如何及時作出響應,是抽獎系統面臨的難題。

為了解決這些問題,我們採用了一些設計方法:

  • 在接入層採用一致性Hash演算法,同一使用者的抽獎請求只會轉發到相同的抽獎系統處理;
  • 抽獎系統採用快取機制,在加快抽獎過程的同時也減少了對儲存層的訪問壓力; 獎品配額機制,平滑抽獎過程,各類獎品按比例有序抽中;

流水和對賬機制,保證抽獎資料最終無差錯發放到使用者賬戶中;

抽獎系統的架構如下圖所示:

(1)快取機制

業務要求在每個刷一刷的活動中,能對使用者中獎次數、參與時間(30秒)進行限制。如果使用者的每個抽獎請求到來時,先到儲存層獲取使用者的中獎歷史資訊,再判定使用者是否還有抽獎資格,在海量高併發的請求場景下,勢必會對儲存層造成巨大的壓力。所以這裡我們引入了本地記憶體快取層,用於儲存使用者的中獎歷史資訊,每次請求到來時,會先到快取層獲取使用者的中獎歷史資訊,如果在快取層沒找到,才會到儲存層獲取,這樣就不會對儲存層造成太大的壓力,同時也能實現業務的需求。快取層我們採用開源Memcached元件實現。

(2)一致性hash定址

紅包抽獎系統是一個分散式的系統,因此為了使快取機制生效,我們在手Q接入層使用了一致性hash的路由演算法進行定址,保證同一個使用者(uin)的請求總會落在同一臺邏輯機器進行處理。

(3)協議處理模組

由於手Q後臺既要處理客戶端的二進位制請求,也要處理其他Web系統的HTTP請求,所以協議處理模組的首要任務就是相容各種格式的協議,給後端模組一個最簡單的結構。為此我們制定了Protobuf格式的互動協議(相容JSON格式,會統一轉換成Protobuf處理),傳給後端模組。

(4)配額管理模組

手Q春節紅包是通過很多場定時“活動”來發放紅包的。每場活動裡面能發放多少現金,能發放多少虛擬物品,發放的比例如何,這些都是配額資料。

更進一步,我們要做到精確控制現金和虛擬物品的發放速度,使得無論何時使用者來參加活動,都有機會獲得紅包,而不是所有紅包在前幾分鐘就被使用者橫掃一空。

配額資訊由配額管理工具負責檢查和修改,每次修改都會生成新的SeqNo。一旦配額agent發現SeqNo發生變化,則會更新本地共享記憶體。由於agent採用雙buffer設計,所以更新完成前不會影響當前業務程序。

(5)抽獎模組

聚焦到抽獎,QQ紅包的抽獎演算法其實並不複雜,但是能否滿足產品需要非常重要。我們的設計思路是至少需要滿足如下需求:

  • 可以在秒級別控制現金和每種物品的發放速度;
  • 可以方便調整現金和每種物品的發放比例;
  • 儘量保證紅包全部發放出去。

為此,我們設計瞭如下的抽獎流程演算法:

需要說明的是,只要是因為配額限制發放紅包失敗,我們都會繼續嘗試給使用者發放其他獎品的紅包,直到沒有獎品可以發放,這樣我們就能保證把獎品儘可能發放出去。

(6)流水系統設計

流水系統用於儲存活動過程中的抽獎流水記錄,在活動後對獎品發放和領用進行統計和對賬。該系統還定時對領用失敗的請求進行重做和對賬,確保獎品發放到使用者賬戶裡。

流水系統架構如下:

由於流水需要記錄使用者中獎的資訊和領用的的情況,資料量巨大,所以抽獎邏輯層本地採用順序寫檔案的方式進行記錄。抽獎邏輯層會定期的把本地的流水檔案同步到遠端流水系統進行彙總和備份,同時,流水系統會對領用失敗的流水進行重做,傳送請求到抽獎邏輯層,抽獎邏輯層會呼叫發貨系統的介面完成發貨操作。

(7)儲存層選型

儲存層的設計向來都是後臺架構設計中的重點和難點。目前騰訊公司內部較成熟的NoSQL儲存系統有CKV、Grocery,經過一番對比我們選擇使用Grocery,主要原因有以下幾點:

強大的帶條件判斷的分散式原子算數運算

抽獎邏輯裡需要對每個獎品進行計數,避免多發少發,所以一個高效可靠的分散式原子加計數器顯得格外重要,Grocery支援帶條件判斷的原子加計數器,呼叫一次介面就能完成獎品計數值與配額的判斷以及獎品計數值的增加;

靈活的資料型別

Grocery支援Key-Key-Row型別的資料儲存格式,可以靈活的儲存使用者的紅包中獎資訊,為獲取使用者單個紅包或者紅包列表提供了豐富的介面;

部署、擴容方便

Grocery有專門的團隊支援,易於部署和擴容。

平滑限頻設計

每一種獎品,對應的業務都有他們自己的容量能力,且各業務的能力也不盡相同(如黃鑽4w/s,京東2k/s等)。為保證紅包活動持續進行,抽獎系統必須嚴格按業務控制派發峰值。派發峰值支援實時可調,避免由於業務方評估不足引起過載。

考慮這樣一種場景,如果請求是在1秒的最開始全部湧到業務方,受限於業務方不同的架構實現,有可能會觸發業務方的頻率限制或者是過載保護。為此,我們將頻限粒度調整到百毫秒,這樣獎品就會在1秒內相對平均的發放,從而解決了上述問題。

3.QQ紅包發貨系統

QQ紅包獎品包括現金和禮券兩類,現金對接財付通,禮券又分騰訊公司內部虛擬物品和第三方禮券。最終禮品落地到使用者的賬戶(QQ錢包餘額、QQ卡券或第三方系統賬戶)中。雖然抽獎系統有作平滑處理,但持續長時間的大流量發貨,也可能導致業務系統不能正常提供峰值下的服務能力。如何承上啟下,既預防抽獎系統不能平滑地發貨導致壓跨發貨系統(自身),又能保護後端業務系統的情況下,以較快的速度將獎品安全發放到賬,是發貨系統的設計要點。發貨系統設計遵循以下策略:

  • 快慢分離
  • 非同步削峰
  • 柔性處理
  • 保護業務系統
  • 最終一致性

發貨系統架構如下圖所示:

快慢分離

現金和禮券後端的系統完全不同,現金通過QQ錢包系統發放入財付通賬戶,要求實時到賬不能延遲。而禮券對接的後端業務千差萬別,服務容量和效能各不相同。為了不讓慢速的禮券發放影響快速的現金髮放,將現金通道與禮券通道分離,互不干擾。

非同步削峰

由於使用者來抽獎的時機完全是隨機的,抽獎系統並不能做到絕對平滑發貨。任由抽獎系統將發貨請求直接透傳到業務系統,將出現不可預知的問題,嚴重時可能會導致業務系統雪崩,這是不能接受的。另外象遊戲禮包類、滴滴券等第三方禮券,可能使用者賬戶並不存在(使用者並不玩該款遊戲,或使用者並沒有第三方賬戶),需要先引導使用者建立賬戶才能發貨,這就要求發貨系統有暫存獎品資訊,具備延後發貨的能力。

發貨系統採用開源的RocketMQ訊息中介軟體作為非同步訊息佇列,暫存發貨請求,再由禮券發貨模組根據各業務的限速配置均勻地呼叫業務介面進行發貨。

柔性處理

禮券類獎品通過非同步方式發放到使用者賬戶,在除夕高峰值可能發放速度跟不上抽獎速度,會延後一些時間才能到賬,這對不明真相使用者可能會造成困擾。因此在使用者中獎資訊頁面中,會提示使用者24小時(或48小時)到賬。發貨過程的每個步驟,都有可以異常失敗,導致發貨不成功,因此在物品詳細頁面的按鈕支援多次發起發貨,在“禮券發貨”模組根據發貨狀態,可以多次嘗試發貨,並保證一個獎品只發放一次。

保護業務系統

前面已經提過,發貨系統通過非同步訊息佇列,將抽獎系統與業務開發隔離開,抽獎洪峰不會直接影響業務系統,對業務系統起來隔離保護作用。

禮券發貨模組針對每個業務單獨配置限速閾值,對各業務的發貨嚴格以不超過限速閾值的速度發放獎品,如果業務有超時或提示超速,再按一定比較再減速。

禮券發貨模組首先會到儲存系統檢查獎品是否真實有效,再到發貨狀態儲存檢查狀態是否正常,只有真正需要的發貨的獎品才向業務系統發起發貨請求,確保發貨的有效性,避免錯發和多發。

最終一致性

由於採用非同步發貨,抽獎時刻獎品不能保證立即發放到使用者賬戶中。但使用者的獎品不會丟失,通過在非同步佇列中暫存,禮券發貨模組逐步以合適的速度將獎品發放到使用者賬戶中。

如果發貨過程中有延時或失敗,使用者可以通過多次領取提起發貨請求,系統支援多次提交。

如果多次發貨仍然失敗,對賬工具第2天會從流水系統中將使用者抽獎資料與發貨資料進行對賬,對發貨異常使用者再次發起發貨。如果對賬仍然失敗,則提醒管理人員介入處理。

二、手Q終端的優化策略

普通使用者不會關心QQ紅包的後臺有多複雜,他們在手Q終端搶紅包時的體驗直接決定著使用者對QQ紅包的評價。對使用者來說,看到紅包後能否順暢的搶和刷,是最直接的體驗痛點,因此需要儘可能降低延遲以消除卡頓體驗,甚至在弱網環境下,也要能有較好的體驗。為了實現該目標,手Q終端採取了以下優化策略:

1.資源預載入

QQ紅包中用到的不經常變化的靜態資源,如頁面,圖片,JS等,會分發到各地CDN以提高訪問速度,只有動態變化的內容,才實時從後臺拉取。然而即使所有的靜態資源都採用了CDN分發,如果按實際流量評估,CDN的壓力仍然無法絕對削峰。因為同時訪問紅包頁面的人數比較多,按83萬/秒的峰值,一個頁面按200K評估,約需要158.3G的CDN頻寬,會給CDN帶來瞬間很大的壓力。為減輕CDN壓力,QQ紅包使用了手Q離線包機制提前把紅包相關靜態資源預載入到手Q終端,這樣可大大降低CDN壓力。

目前手Q離線包有兩種預載入方式:

  • 將靜態資源放入預載入列表,使用者重新登入手Q時監測離線包是否有更新並按需載入(1天能覆蓋60%,2天能覆蓋80%,適合預熱放量情況)。
  • 主動推送離線包,向當前線上使用者推送離線包。(2個小時可以完成推送,覆蓋總量的40%左右,適合緊急情況)通過離線包預載入後,除夕當天的CDN流量並沒有出現異常峰值,比較平穩。

2.快取和延時

2.59 億使用者同時線上,使用者刷一刷時的峰值高達83萬/秒,如果這些使用者的操作請求全部同時擁向後臺,即使後臺能抗得住,需要的頻寬、裝置資源成本也是天文數字。為了儘可能減輕後臺伺服器壓力,根據使用者刷一刷的體驗,使用者每次刷的操作都向後臺發起請求是沒有必要的,因此手Q在終端對使用者刷一刷的操作進行計數,定時(1~3秒)非同步將彙總資料提交到後臺抽獎,再將抽獎結果回傳到手Q終端顯示。這樣既保證了“刷”的暢快體驗,也大大減輕後臺壓力,抽獎結果也在不經意間生產,使用者體驗完全無損。

3.錯峰

對使用者進行分組,不同組的使用者刷一刷紅包(企業明星紅包、AR紅包等)的開始時間並不相同,而是錯開一段時間(1~5分鐘),這樣通過錯開每一輪刷紅包的開始時間,可以有效平滑使用者刷一刷的請求峰值。

4.動態調整

手Q終端和後臺並不是兩個孤立的系統,而是一個整體。手Q系統搭建有一整套的負載監控體系,當後臺負載升高到警戒線時,手Q終端可以根據後臺負載情況,動態減少發向後臺的請求,以防止後臺出現超載而雪崩。

5.總量限制和清理

在刷一刷紅包和AR紅包過程中,當用戶已經抽中的獎品數達到一個限值(例如5個),使用者不能再中獎,這時使用者的抽獎請求不再向後臺傳送,而是終端直接告知使用者“未中獎,請稍後再試”,和清除AR紅包地圖中的紅包顯示。後續更新《QQ紅包技術方案全解密(二)》