上一期爲您解密了8億月活的QQ後臺服務接口隔離技術《QQ18年,解密8億月活的QQ後臺服務接口隔離技術》,那你是否好奇春節期間全網滲透率高達52.9%的ARQQ紅包後臺是怎樣的一番光景呢?本文爲您揭曉。html
剛剛過去不久的QQ「LBS+AR」天降紅包玩法在5天內達成了2.57億用戶參與、共計領取卡券和現金紅包20.5億次的成績,獲得用戶的一致好評,贏得口碑、數據雙豐收。算法
做爲一個全新的項目,後臺面臨着許多挑戰,好比:數據庫
1. 海量地圖活動數據管理緩存
在全國960萬平方千米土地上投放上億的活動任務點,如何管理海量的的地圖/任務數據?性能優化
2. 地圖查點方案服務器
如何根據用戶地理位置快速獲取附近可用的任務點?數據結構
3. 數千個行政區紅包餘量的實時統計架構
紅包雨活動是在區級行政區開展的,全國有數千個區級行政區,每一個行政區都有數十個不一樣的紅包任務及獎品,實時返回數千個地區的紅包剩餘量是如何作到的?併發
本文將從整體上介紹LBS+AR紅包後臺系統架構,並逐步解答上述幾個問題。框架
1. 投放系統:活動、商戶、獎品等信息的可視化配置頁面
2. 靜態數據層:CDB提供的MySQL服務,包含多個配置表
3. 配置同步系統:負責構建海量活動數據的緩存及同步,包含2個模塊
a) 緩存構建模塊:按期輪詢式地從CDB中讀取活動數據,若是數據發生變化,從新構建AR標準緩存格式的共享內存數據
b) 配置同步服務端:負責接收客戶端請求,將共享內存數據下發到各個業務機器
4. 邏輯層:負責接受客戶端請求,包含2個系統
a) 主邏輯:負責用戶參加地圖紅包的核心邏輯,包含地圖查點、抽獎等流程
b) 採集系統:負責實時獲取各個活動及獎品的發放數據,用於主邏輯獲取活動狀態、客戶端顯示剩餘計數、後臺統計活動數據
5. 動態數據層:負責用戶、活動動態數據的存儲,包含4類數據
a) 發獎計數器:每一個任務/獎品發放量
b) 用戶歷史記錄:用戶中獎的信息
c) 冷卻與限額:用戶領取的每種獎品的限制信息
d) 疊放計數器:可重複獲取的獎品數量(本次活動是皇室戰爭卡券數)
6. 輔助模塊:完成單個特定任務的模塊
a) 頻率控制:負責控制獎品發放速度
b) 訂單:負責管理財付通現金訂單
c) 一致接口:負責一致性路由,將同一個用戶的請求路由到同一臺機器的指定進程
7. 發獎相關係統:負責獎品發放的外部系統
下面主要爲你們介紹海量配置管理、地圖打點查點和實時的餘量採集系統。
1、海量配置數據管理
LBS+AR紅包與以往的紅包最大的不一樣在於多了一重地理位置關聯,全國有上千萬的地理位置信息,結合活動的任務獎品數據產生了海量的配置數據,而這些數據都須要快速地實時讀取(詳細數據結構見後文)。這是後臺遇到的第一個挑戰。
整體思路
配置數據有以下特色:
1. 數據量可能很大,數據間有緊密的關聯
2. 「一次配好,處處使用」,讀量遠高於寫量
根據第一個特性,因爲咱們有Web投放系統的可視化配置能力,而公司內部的CDB能提供可靠的MySQL服務,有自動容災和備份的功能,故選擇CDB做爲靜態數據存儲。
根據第二個特性,咱們須要開發一種緩存,放棄寫性能,將讀性能優化到極致。
第一代緩存系統——寫性能換讀性能
緩存系統將數據讀取方式從「遠端磁盤讀取」改成「本地內存讀取」,讀性能提升好幾個數量級,它具備以下特色:
1. 緩存構建模塊定時輪詢數據庫,在共享內存中構建緩存,配置變更可在分鐘級時間內完成
2. 每張表使用2塊共享內存,一塊用於實時讀,另外一塊用於更新,數據更新無感知,對業務零影響
3. 使用二分法查詢數據,O(logN)的複雜度,性能較優
第二代緩存系統——O(logN)算法部分變O(1)
因爲在地圖查點流程中要執行數十至上百次的「任務與POI關聯數據」查詢,而對億級數據進行二分查找每次要作將近30次的字符串比較,嚴重影響了系統性能。
爲了解決這個問題,第二代系統針對POI數據離散化的特色,對量大的數據進行了前綴哈希,將一半的O(logN)操做轉換成O(1),進一步用寫性能換讀性能,性能獲得有效提高,字符串比較的最大次數減小了將近一半。
算法流程:
1. 根據經驗設置前綴長度
2. 遍歷數據構造映射表,映射表存儲了前綴及其對應的起始/末尾序號
3. 之前綴爲Key,將映射表記在多階哈希中(指定大小,枚舉階數搜索可行的壓縮方案)
下面是一個構造映射表的例子:
對於樣例數據
● 若是取3字節前綴,只有2個結果,產生的映射表比較容易構造哈希,但最大單條映射的記錄長度是9,二分的次數仍然較多。
● 若是取5字節前綴,最大單條映射的記錄長度是4,二分次數較少,但條目較多,哈希構造較難。
總結以下
一些可能的問題:
● 爲何不所有哈希呢?
上億條數據每次改動都作所有哈希,耗費的時間和空間恐怕是天文數字。雖然咱們捨棄寫性能換取讀性能,但用幾個小時(幾天)寫耗時換幾納秒的讀耗時,邊際效用已經降到負數了。實踐中作一半便可達到不錯的讀寫平衡。
● 若是映射的記錄長度十分不均勻怎麼辦?
這是此項優化的命門所在。幸運的是咱們要優化的數據是POI相關的,而實踐發現POI數據離散性極好,得出的映射記錄數量很是均勻
● 若是哈希一直構造失敗怎麼辦?
此項配置數據改動很少,若是對於某一版本數據構造失敗,通常會有足夠的時間根據數據特性調整前綴長度、增長哈希表大小、擴大階數搜索範圍來確保成功。若是時間比較緊急,也能夠放棄此項優化,程序若是檢測到哈希失敗,會自動使用所有二分的方式讀取。即使沒有這項優化,咱們還有許多柔性調控策略防止系統過載。
第三代緩存系統——中間層加速同步
上一代系統擴容後的結構以下圖:
每臺機器構建數據時都要去數據庫讀一次全量數據並排序,同時要在本地生成,每次大約耗時5分鐘。而在這種架構下,全部機器的讀數據庫操做實際上就是串行的,當邏輯層擴容到100臺機器時,完成所有任務將耗費好幾個小時,考慮到配置修改的風險,這種架構在實踐上是不可行的。
第三代架構圖以下(箭頭指數據流向):
在數據層和邏輯層中間添加一個配置同步系統,先讓少部分配置中心機器優先完成構建,再去帶動數量較多的邏輯層機器,最終達到共同完成。有效解決了上一代平行擴容的問題,效果拔羣。
實踐效果
1. 極致讀性能
2. 海量靜態緩存數據的快速構建:完成所有機器近20G不一樣類型靜態數據的構建和同步只須要30分鐘
3. 數據同步無感知,實現無縫切換,對業務零影響
2、地圖打點與查點
基於LBS的活動離不開地理位置相關的業務交互。在本次活動中,用戶打開地圖會按期向後臺上報座標,後臺須要根據座標獲取周圍可用的活動任務點,此節介紹打點與查點相關內容。
專業的地圖服務會使用一種樹形數據結構來進行空間查詢,但LBS+AR紅包活動的場景比較簡單,故而選用了一種粒度較粗性能更好的打點查點方案,查詢附近地理信息只須要進行四則運算,再一次用O(1)的方法解決O(logN)的問題。
地圖格子方法介紹
將整個二位平面根據座標分紅一個個邊長相等的正方形格子,根據用戶的座標用簡單的數學運算便可獲取相應的格子ID,時間複雜度O(1)。一個格子是一次查詢的最小粒度。
在本次活動中,咱們使用約200米邊長的格子,每次查詢會返回以用戶爲中心周圍共計25個格子的任務點。
格子與任務點示例以下:
打點流程介紹
活動的投放是以任務的維度投放,每一個任務關聯一個POI集合,每一個POI集合中包含幾個到上百萬不等的POI點,每一個POI點都有一個經緯度信息(詳細狀況見下文數據結構設計)。
打點的責任是將任務爲索引的數據重構爲以格子ID爲索引的數據,經過遍歷緩存系統中的POI/POI集合/任務分片來實現。最終的格式以下:
查點流程介紹
1. 客戶端上報經緯度
2. 根據經緯度計算中心格子ID
3. 根據中心格子ID及半徑配置,獲取所有格子列表
4. 在打點系統中得到此片區域所有Poi和Task信息
5. 檢查任務狀態後返回給客戶端
3、採集系統進化之路
採集系統的主要職責是:
1. 實時返回區級行政區紅包計數
2. 實時接受主邏輯的查詢,返回獎品發放狀態。
3. 返回活動預告以及參數配置等輔助信息。
這裏面臨的主要的挑戰是區級行政區的紅包餘量計數,本文將着重介紹餘量計數方案的演化思路。
樸素方案
來一個請求就去讀一次!
進程級緩存方案
上一個方案顯然不可行,而進程級緩存是最初使用的方案。這時採集功能並未單獨成爲一個獨立模塊,而是嵌在主邏輯裏的。
主邏輯按期地掃描配置中所有有效任務,讀計數器,將計數存儲在STLMAP中。
假如每次數據緩存5秒,實際活動中約有8w條數據須要處理,天天活動分8場,100臺邏輯層機器,對數據層的壓力是400w次每秒,這個級別的讀量幾乎佔滿了所有Grocery性能。雖然方案比較成熟,但仍是決定優化。
機器級緩存方案
這個方案作了兩件事:
1. 將進程級的緩存上升到機器級,節省40倍進程訪問開銷
2. 將採集流程從主邏輯解耦,單獨部署,減輕100臺主邏輯機器綁定的訪問開銷
本方案使用有序的拼接索引+範圍二分查找的方式替代STLMAP進行查詢,使用sf1框架實現,服務與構造進程一體。因爲sf1不支持併發外部調用,構造進程使用簡單逐個查詢的方法處理。
(sf1是後臺較爲經常使用的一種服務框架,性能較好,但不支持自然異步開發)
夾縫求生的最終優化
上一方案功能上確實可行,但性能上仍然存在問題:sf1框架很差作併發外部調用,用串行的方式查詢數萬條數據,完成一輪更新的時間是分鐘級的,影響產品體驗。
爲了優化此問題,咱們作了兩級併發:
1. 利用Grocery提供的MultiKeyBatch方法,讓Grocery接口機併發(詳情請參見Grocery API,調用此接口格外注意業務數據大小及打包數量,輕易不要使用!)。示意圖以下:
2. 將構造進程從sf1改成spp,利用mt_task方法併發請求。咱們使用了宏定義IMTTask的簡便用法。
須要注意的是,理論上能夠只用第二種併發方式便可知足「採集模塊」的需求,但從整個系統的角度看,防止數據層過載更加劇要,第一種併發方式減小了10倍Grocery Intf請求量和一部分Cache請求量,雖然開發量較大,倒是不可或缺的。
通過兩級併發,分鐘級的構建間隔被縮短到了1秒。可是不能發佈!由於遇到了夾縫問題。
採集模塊夾在客戶端和Grocery之間,加速採集會影響Grocery,而減小機器又會影響客戶端更新計數的效果,三個條件互相制約,須要想辦法突破這個夾縫難題。
解決方法是:將查餘量過程的O(logN)流程變成O(1),性能提高10倍便可。
採集系統的主要業務邏輯是返回地區紅包計數,以前的方案1秒內的數萬次請求每次都會執行包括二分查找在內的全套邏輯,而事實上只有第一次是有必要的(地區的餘量等信息1秒內的變化微乎其微)。那麼答案就呼之欲出了,只須要給每一個地區的結果作1秒鐘的緩存便可。
最終把能夠作緩存的流程全都加上了合理的緩存結構,單機性能成功提高10倍。
本次項目總結的後臺開發基本法:
1. 架構問題能夠經過讀寫轉換、時空轉換的方式變成算法問題
2. O(logN)問題總有辦法變成O(1)問題
3. 沒人能預知將來。開發海量數據/海量請求的系統時,多用上面兩個方法,從一開始就要盡力作到最好。
另外不能忘了,在海量用戶來襲的前夕,對本身的產品承載能力進行一個測試,及時作好調整和優化。任何企業都是同樣,不要萬事俱備,卻忽略了這個環節,讓用戶乘興而來,敗興而歸···
騰訊提供了一個能夠自主進行服務器性能測試的環境,用戶只須要填寫域名和簡單的幾個參數就能夠獲知本身的服務器性能狀況,目前在騰訊WeTest平臺能夠無償使用。
騰訊WeTest服務器性能測試運用了沉澱十多年的內部實踐經驗總結,經過基於真實業務場景和用戶行爲進行壓力測試,幫助遊戲開發者發現服務器端的性能瓶頸,進行鍼對性的性能調優,下降服務器採購和維護成本,提升用戶留存和轉化率。
功能目前免費對外開放中,點擊連接:http://wetest.qq.com/gaps 可當即體驗!
若是對使用當中有任何疑問,歡迎聯繫騰訊WeTest企業qq:800024531