版權聲明:本文由吳逸翔 原創文章,轉載請註明出處:
文章原文連接:https://www.qcloud.com/community/article/876818001486611841前端
來源:騰雲閣 https://www.qcloud.com/community算法
導語 大哥說,今年手Q遊戲的春節紅包你來作。那該怎麼作?以及怎麼作才能讓大哥放心?本文從後臺的角度出發講述了這個過程和方法,對於關鍵的前臺部分也有所涉及。緩存
1.需求背景安全
2.需求分析網絡
3.總體方案與項目分解架構
4.需求開發併發
5.系統保障app
6.演習驗證異步
7.總結高併發
2017 年的手 Q 春節遊戲紅包共有刷一刷/ AR 地圖/掃福三種,以下圖所示:
雖然紅包分三種,但在遊戲業務側這邊的體驗都是同樣:用戶獲得一個紅包卡券,打開後展現一個(刷一刷紅包)或者多個(AR 地圖紅包)遊戲的禮包列表,用戶選擇一個禮包後彈出區服組件,用戶確認對應的區服角色信息後會禮包會在 48 個小時內發放到帳。體驗以下:
遊戲紅包的設計容量爲入口卡券頁流量 80k/s,以上體驗流程一共涉及三個後臺接口:
這個功能使用現有能力比較容易解決。活動共有十種遊戲,每一個遊戲有兩種禮包:拉新(面向非註冊用戶,價值 80 元)/拉活躍(面向註冊用戶,價值 20 元),一個用戶只能得到這兩種禮包中的一種,產品策略容許拉新的用戶得到價值較低的拉活躍禮包,反之則不容許。頁面展現按用戶偏好排序十個遊戲,每一個遊戲展現一個拉新禮包或者一個拉活躍禮包。
出於下降除夕當前流量負載和柔性考慮,在紅包活動前,十種遊戲的禮包內容做爲前端靜態數據已經預先經過離線包/CDN 下發;紅包活動時,後臺接口根據用戶偏好返回的遊戲禮包列表,只是提供前端禮包內容進行過濾和排序,失敗了也有前端默認的遊戲禮包列表,保障用戶體驗。
過濾:讀取存儲,用戶有註冊的遊戲返回活躍禮包,用戶沒有註冊的遊戲返回拉新禮包。
排序:一個兩層排序,第一層排序讀取存儲(key 爲用戶,value 爲用戶所註冊的遊戲列表),用戶註冊的遊戲(拉活躍)排在用戶沒有註冊的遊戲(拉新)前面;第二層排序,對於拉新遊戲列表和拉活躍遊戲列表內部,使用神盾算法對用戶這10款遊戲的偏好再進行二次排序。對於外部接口的依賴只有 CMEM 存儲和神盾算法接口,這兩個接口以及合併這兩種數據給出最終的個性化推薦禮包列表接口均可以平行擴容以支持 100k 級別的 QPS。
這個功能是現有能力。這個角色信息的來源是 IDIP ,但因爲該接口較緩慢(2s 左右)且容量較低(低於 10k/s),故後臺作了一層緩存,將 IDIP 的區服信息永久性緩存到 CMEM 中,前臺也有本地緩存,在實踐中,前臺緩存命中率爲 60%,後臺爲 35%,多級緩存後走到 IDIP 的請求量只有5%,對 IDIP 影響不大,只須要擴容現有的區服 server 和 CMEM 便可。
這個功能使用現有能力解決存在困難。遊戲中心平常發貨的道具和平臺比較多,平臺分爲 IEG-AMS/MP 兩種,MP 發貨對於發遊戲道具和發 Q 幣又是兩種接口,故咱們在架構上使用 Facade 模式,使用 AMS 做爲發貨 proxy,屏蔽了底層發貨的複雜性,向遊戲中心提供統一的發貨接口,但比較遺憾的是從AMS 到遊戲的發貨接口都是同步接口,發貨能力較低,發貨能力最高的王者榮耀也只承諾了 3k/s 的發貨速度,明顯不足以直接承受 100k/s 級別的紅包發貨,故這裏的核心問題是須要有一個隊列來解決生產/消費速度不對等的問題。
去年的紅包是後臺收到發貨請求後落地到本地文件返回用戶成功,再由一個本機的 daemon 跑落地文件按遊戲方所能提供的發貨速度進行實際發貨,至關於使用本地隊列緩衝。但這個方案存在某臺機器掛掉後若是不能恢復會丟失一部分本地的發貨數據形成漏發,以及每一個高併發業務都要從新作這一套東西不方便通用的問題。
從架構上思考,其實最合理的方案是做爲發貨 proxy 的 AMS 提供異步發貨的能力,將用來解決生成/消費速度不匹配的 MQ 作在 AMS 內部,爲業務提供通用的異步發貨能力,業務側就不須要考慮發貨超過遊戲方能力的問題,新業務有相似的場景也不須要從新開發。
遊戲中心是業務側,AMS 是平臺側的能力,屬於另外一箇中心的業務,因而一開始咱們準備推進 AMS 作異步發貨的能力,這樣業務就只要調用發貨接口就能夠了,非常方便。但事情並無想象中順利,與 AMS 的開發和 PM 開會溝通了幾回,異步發貨的能力他們也有作技術規劃,但年前他們有其它需求要作,沒有時間支持。和 leader 討論了一下這個能力最好仍是放在 AMS 作成通用以便之後有一樣場景的業務使用,前臺也有同窗開發過 AMS 功能,能夠由遊戲中心業務側的先後臺同窗合做完成 AMS 異步發貨功能的開發,在春節紅包中應用,再將這個功能交接給平臺側的同窗維護,達到共贏的效果。
總體方案圖如上圖所示,因爲整個項目涉及多方開發,並且模塊較多,整個模塊的開發週期較長,做爲一期開發的話沒法跟上基礎側卡券的驗收和安排的幾回演習/壓測,故按「大系統小作」的原則,根據模塊的重要和緊急程度分爲幾期迭代完成,每一期有獨立的里程碑目標並達到對應的驗收/演習/壓測要求:
第一期(方案圖左側部分)爲功能需求,在 12 月 9 號上線經過卡券方面功能驗收,先使用當前的同步發貨接口,對性能無特別要求。
第二期(方案圖右側偏左部分)爲性能需求,在 12 月 20 號上線參加第一次演習,對發貨進行異步化改造,要求直接面向用戶的外網發貨接口能支持 100k QPS 的峯值流量。
第三期(方案圖右側偏右部分)爲容錯需求,在 12 月 27 號上線參加第二次演習,對發貨進行對帳補送改造,保證發貨的可靠性。
第四期爲監控需求,在 1 月 6 號上線參加第三次演習,確認各項關鍵數據的採集,並將採集到的數據展示到一個統一視圖上,以便除夕期間值班人員實時瞭解紅包系統的總體運行狀況和出數據報表。
{3.1 後臺禮包推薦接口}爲用戶推薦禮包,用戶領取時須要通過{4.1 AMS 外網發貨新 OP}校驗領取資格,後臺的推薦數據必須能和 AMS 的資格校驗數據可以對上,不然會出現後臺推薦的禮包用戶領取時卻通不過 AMS 的資格校驗致使領取不了的問題。
{4.1 AMS 外網發貨新 OP}接口處理的是單個遊戲的領取禮包的請求,資格校驗操做判斷一個用戶是否註冊了某個遊戲。這個是 AMS 現有的通用功能,數據存儲在 AMS 的 CMEM 中,簡化描述就是一個 key-value 模型,key 爲 uin+appid,value 若是有註冊則爲 1,沒有則爲 0(實際爲了節省存儲空間,使用 bitmap 桶實現,具體參見號碼包系統使用文檔),導入的數據源是產品在除夕前一週提供 10 款遊戲的全量註冊號碼包,每一個遊戲一個文件,文件內容是註冊用戶的 QQ 號。
但{3.1 後臺禮包推薦接口}接口返回的是多個遊戲的禮包列表,須要獲取十個遊戲的用戶註冊狀態。若是讀取 AMS 現有的接口/存儲,會有兩個問題:
後臺將號碼包數據進行從新組織存儲到後臺申請的另一個 CMEM 中,key 爲 uin,value 爲用戶已註冊的 appid 列表,已註冊的遊戲推薦拉活躍禮包,沒註冊的遊戲推薦拉新禮包,這樣只須要查詢一次 CMEM 就能夠獲得十個遊戲每一個遊戲的禮包推薦類型是拉新仍是拉活躍。
因爲 AMS 和後臺使用的是同一份號碼包數據,只是應用場景不一樣,數據組織形式不一樣,兩份 CMEM 數據同質異構,故後臺推薦的禮包能夠經過 AMS 的資格校驗。
紅包活動具備時間短(單場 5~30 分鐘)、大用戶量參與(1.5 億+)參與的特性,請求併發高,遊戲紅包入口流量設計爲 80k/s,流經各個模塊有衰減也有增幅,最終用戶領取禮包請求預估爲 96k/s,而遊戲方提供的十款遊戲總髮貨能力只有 5k/s(單款遊戲最大爲王者榮耀 3k/s),請求峯值接近處理能力的 20 倍,同步調用會致使遊戲方發貨接口過載,形成大面積發貨失敗,這個問題如何處理?
使用一個緩衝隊列來解決生產消費能力不對等的問題。用戶領取請求到達 AMS 進行基礎的資格校驗後將請求放入 MQ 中,返回用戶成功並告知會在 48 小時內到帳。再由後臺發貨 Daemon 從 MQ 中讀取請求,經過限速組件控制保證以不超過遊戲方發貨能力的速率進行發貨操做。使用的 MQ 是部門近來建設的 RocketMQ,具體參見會員消息隊列( RocketMQ )接入指南。
三場活動發放的禮包總數預計將近 4 億,如何保障這些禮包對於合法用戶能都發貨到帳,很多發也很少發?如何防範高價值道具被惡意用戶刷走?有沒有可能內部開發人員本身調用接口給本身發禮包?
用戶領取禮包的接口{4.1 AMS 外網發貨新 OP}調用成功,會爲這個請求附帶一個 UUID 生成的一個全局惟一的訂單號,再放進 MQ 中,{4.3 AMS 內網發貨 OP}從 MQ 中取出消息,調用遊戲方發貨接口前都會先校驗這個訂單號是否用過,沒用過則將訂單號以 key 的形式寫入 CMEM,再進行發貨操做。若是出現對同一個發貨消息進行重複發貨,則會發現訂單號已經用過了不會進行實際的發貨操做,保證以訂單號爲標識的同一個發貨請求只會進行一次發貨操做。
發貨失敗是不可避免的,諸如網絡波動/遊戲方發貨接口故障之類的問題均可能致使調用發貨接口失敗。在同步領取環境下,用戶能夠經過重試在必定程度上解決這個問題。可是對於異步發貨,用戶點擊領取後發貨請求由{4.1 AMS 外網發貨新 OP}放入 MQ 中就算成功了,即便後臺調用遊戲的實際發貨接口失敗了沒有實際到帳,用戶對此也無感知不能進行重試可是會投訴,後臺發貨系統必須經過自身的容錯保證即便遊戲方的發貨接口不穩定偶爾會失敗,用戶所領的禮包能最終到。這裏咱們使用了對帳補送方案。
對帳:用戶領取禮包調用的接口{4.1 AMS 外網發貨新 OP}成功寫應發流水,{4.3 AMS 內網發貨 OP}調用遊戲方發貨接口的寫實發流水,因爲部分消息會堆積在消息隊列中,這部分稱爲隊列堆積流水。故實際要進行補發操做的流水由如下公式可得:
失敗補發流水= 應發流水 - 實發流水 - 隊列堆積流水。
因爲訂單號的存在,能夠保證同一個發貨請求重複發送也不會多發,對隊列中堆積的消息提早進行補發操做也不會致使多發。故當隊列中堆積的流水較少的時候,採用應發流水與實發流水的差集做爲失敗補發流水是合理,只是每一個對帳週期會對隊列中堆積的消息進行兩次發貨操做,對性能略有損耗。
後臺每一個小時運行一次增量對帳功能,檢測 MQ 消息堆積量量低於某個閾值,則進行對帳操做,截取上次對帳到此時的應發流水/實發流水,二者相減獲得補發流水。
補送:對對帳操做獲得的補發流水調用遊戲方發貨接口進行發貨補送操做。
對於領獎的請求,都要求都要求帶上登陸態,對用戶進行身份驗證,同時對於高價值的道具開啓安全打擊,上報安全中心進行惡意用戶校驗,防止被惡意用戶刷走。
對於發貨的機器都要安裝鐵將軍,用戶須要使用 RTX 名和 token 才能登陸機器,審計用戶在機器上的操做行爲;
發貨模塊對於調用方是須要嚴格受權,調用方須要申請 key,包含程序路徑、程序 MD五、部署模塊等信息,保證發貨功能不被隨意調用。
在監控方面有兩個主要訴求:
(1)咱們對外提供的服務是否正常?若是有問題,如何快速地發現問題、分析問題?
(2)實時知道用戶在整個系統的行爲漏斗模型,每一步的轉化率是多少?
遊戲紅包涉及紅包基礎側/業務前臺/業務後臺/AMS/MQ 平臺等多個合做方,各個系統有本身的監控系統,數據來源不一致,活動當天一個系統一個系統地收集的話效率過低。
紅包做爲一個涉及多個子系統的聚合系統,咱們須要一個彙總了各個子系統關鍵數據的總體視圖,纔可以較全面地監控業務核心指標,對系統和業務有較全面把控,避免在監控系統中跳轉檢索而耗費有限的時間,爲迅速響應解決問題提供保證。
接口封裝:雖然紅包涉及的多個子系統,各自有各自的上報方式和監控系統,可是對於關鍵數據大都有提供 HTTP 形式的查詢接口,咱們經過封裝,將接口的定義統一爲 key-value 形式,以(監控項 id,開始時間,結束時間)爲 key,value 爲(開始時間,結束時間)段內監控id的值之和。
配置化:一場紅包活動的監控,能夠由一個時間段加若干個監控項定義。好比刷一刷紅包,時間段爲除夕當天 20:00~20:30,監控項爲若干頁面的點擊量,若干禮包的發放量,若干後臺接口的請求量,若干 MQ 的堆積量等等。
經過對接口的封裝和配置化,新增一場紅包活動,只須要增長一個時間段和若干個監控項的配置文件,好比下圖的 AR/刷一刷混場/刷一刷專場就是經過 3 個配置文件定義 3 場活動,新增一場活動也只須要增長一個配置文件,並能夠在一個視圖上靈活切換,至關方便。
從上圖中咱們就能夠實時看到實發和應發是大體相等的,隊列沒有出現堆積,用戶在各級頁面的轉化率,能夠很方便地判斷系統的健康狀態和分析定位問題。