FunData — 電競大數據系統架構演進


背景來源:FunData做爲電競數據平臺,v1.0 beta版本主要提供由Valve公司出品的頂級MOBA類遊戲DOTA2相關數據接口(詳情:open.varena.com)。數據對比賽的觀賞性和專業性的提升起到相當重要的做用。本文由IT大咖說(微信id:itdakashuo)整理,經投稿者與嘉賓審閱受權發佈。git

閱讀字數:4822 | 13分鐘閱讀github

摘要

本文將介紹FunData的架構演進中的設計思路及其涉及的相關技術,包括大數據流處理方案、結構化存儲轉非結構化存儲方案和數據API服務設計等。算法

不管是對於觀賽用戶,仍是比賽用戶,電競數據的豐富程度與實時要求獲得愈來愈多的關注。後端

電競數據的豐富性從受衆角度來看,可分爲賽事、戰隊和玩家數據;從遊戲角度來看,維度可由英雄、戰鬥、道具以及技能等組成;電競數據的實時性包括賽前兩支戰隊的歷史交戰記錄、賽中的實時比分、勝率預測、賽後比賽分析和英雄對比等。緩存

所以多維度的數據支持,TB到PB級別的海量數據存儲與實時分析都對底層系統的架構設計有着更高的要求,亦帶來了更嚴峻的挑戰。bash

1.0架構

項目發展初期,依照MVP理論(最小化可行產品),咱們迅速推出FunData的初版系統(架構圖如圖1)。系統主要有兩個模塊:Master與Slave。微信

Master模塊功能以下:架構

  • 定時調用Steam接口獲取比賽ID與基礎信息併發

  • 經過In-Memory的消息隊列分發比賽分析任務到Slave節點負載均衡

  • 記錄比賽分析進度,並探測各Slave節點狀態

Slave模塊功能以下:

  • 監聽隊列消息並獲取任務(任務主要爲錄像分析,錄像分析參考github項目clarity(https://github.com/skadistats/clarity)與manta(https://github.com/dotabuff/manta))

  • 分析數據入庫

系統上線初期運行相對穩定,各維度的數據均可快速拉取。然而隨着數據量的增多,數據與系統的可維護性問題卻日益突出。

  1. 新增數據字段須要從新構建DB索引,數據錶行數過億構建時間太長且形成長時間鎖表。

  2. 系統耦合度高,不易於維護,Master節點的更新重啓後,Slave無重連機制須要所有重啓;同時In-Memory消息隊列有丟消息的風險。

  3. 系統可擴展性低,Slave節點擴容時須要頻繁的製做虛擬機鏡像,配置無統一管理,維護成本高。

  4. DB爲主從模式且存儲空間有限,致使數據API層須要定製邏輯來分庫讀取數據作聚合分析。

  5. 節點粒度大,Slave可能承載的多個分析任務,故障時影響面大。

圖1 1.0 ETL 架構圖

在開始2.0架構設計與改造前,咱們嘗試使用冷存儲方法,經過遷移數據的方式來減輕系統壓力(架構設計如圖2)。因爲數據表數據量太大,併發多個數據遷移任務須要大量時間,清理數據的過程一樣會觸發從新構建索引,方案的上線並無根本性地解決問題。

圖2 冷存儲方案

2.0架構

吸收1.0系統的經驗,在2.0架構設計中,咱們從維護性、擴展性和穩定性三個方面來考慮新數據系統架構應該具有的基本特性:

  • 數據處理任務粒度細化,且支持高併發處理(全球一天DOTA2比賽的場次在120萬場,錄像分析相對耗時,串行處理會致使任務堆積嚴重)

  • 數據分佈式存儲

  • 系統解耦,各節點可優雅重啓與更新

圖3 2.0ETL總架構圖

2.0系統選擇Google Cloud Platform來構建整個數據ETL系統,利用PubSub(相似Kafka)做爲消息總線,任務被細化成多個Topic進行監聽,由不一樣的Worker進行處理。這樣一方面減小了不一樣任務的耦合度,防止一個任務處理異常致使其餘任務中斷;另外一方面,任務基於消息總線傳遞,不一樣的數據任務擴展性變得更好,性能不足時可快速橫向擴展。

任務粒度細化

從任務粒度上看(如圖3),數據處理分爲基礎數據處理與高階數據處理兩部分。基礎數據,即比賽的詳情信息(KDA、傷害與補刀等數據)和錄像分析數據(Roshan擊殺數據、傷害類型與英雄分路熱力圖等數據)由Supervisor獲取Steam數據觸發,通過worker的清理後存入Google Bigtable;高階數據,即多維度的統計數據(如英雄、道具和團戰等數據),在錄像分析後觸發,並經過GCP的Dataflow和自建的分析節點(worker)聚合,最終存入MongoDB與Google Bigtable。

從Leauge-ETL的細化架構看(如圖4),原有的單個Slave節點被拆分紅4個子模塊,分別是聯賽數據分析模塊、聯賽錄像分析模塊、分析/挖掘數據DB代理模塊和聯賽分析監控模塊。

  1. 聯賽數據分析模塊負責錄像文件的拉取(salt、meta文件與replay文件的獲取)與比賽基本數據分析

  2. 聯賽錄像分析模塊負責比賽錄像解析並將分析後數據推送至PubSub

  3. 分析/挖掘數據DB代理負責接收錄像分析數據並批量寫入Bigtable

  4. 聯賽分析監控模塊負責監控各任務進度狀況並實時告警

同時全部的模塊選擇Golang重構,利用其「天生」的併發能力,提升整個系統數據挖掘和數據處理的性能。

圖4 League-ETL架構

分佈式存儲

如上文提到,1.0架構中咱們使用MySQL存儲大量比賽數據及錄像分析數據。MySQL在大數據高併發的場景下,總體應用的開發變得愈來愈複雜,如沒法支持schema常常變化,架構設計上須要合理地考慮分庫分表的時機,子庫的數據到必定量級時面臨的擴展性問題。

參考Google的Bigtable(詳情見Big table: A Distributed Storage System for Structured Data)及Hadoop生態的HBase(圖5),做爲一種分佈式的、可伸縮的大數據存儲系統,Bigtable與HBase能很好的支持數據隨機與實時讀寫訪問,更適合FunData數據系統的數據量級和複雜度。

圖5 Hadoop生態

在數據模型上,Bigtable與HBase經過RowKey、列簇列名及時間戳來定位一塊數據(Cell)。(如圖6)

(rowkey:string,columnfamily:columnstring,timestamp:int64)→value:string複製代碼

圖6 數據索引

例如,在FunData數據系統中,比賽數據的RowKey以hash_key+match_id的方式構建,由於DOTA2的match_id是順序增大的(數值自增量不惟一),每一個match_id前加入一致性哈希算法算出的hash_key,能夠防止在分佈式存儲中出現單機熱點的問題,提高整個存儲系統的數據負載均衡能力,作到更好的分片(Sharding),保障後續DataNode的擴展性。

(如圖7) 咱們在hash環上先預設多個key值做爲RowKey的前綴,當獲取到match_id時,經過一致性哈希算法獲得match_id對應在hash環節點的key值,最後經過key值與match_id拼接構建RowKey。

RowKey=Hash(MatchID)+MatchID=Key_n+MatchID複製代碼

圖7 一致性hash構建RowKey

時間戳的使用方便咱們在聚合數據時對同一個RowKey和Column的數據重複寫入,HBase/Bigtable內部有自定的GC策略,對於過時的時間戳數據會作清理,讀取時取最新時間節點的數據便可。

這裏你們可能會有個疑問,Bigtable與HBase只能作一級索引,RowKey加上hash_key以後,是沒法使用row_range的方式批量讀或者根據時間爲維度進行批量查詢的。在使用Bigtable與HBase的過程當中,二級索引須要業務上自定義。在實際場景裏,咱們的worker在處理每一個比賽數據時,同時會對時間戳-RowKey構建一次索引並存入MySQL,當須要基於時間批量查詢時,先查詢索引表拉取RowKey的列表,再獲取對應的數據列表。

在數據讀寫上,Bigtable/HBase與MySQL也有很大的不一樣。通常MySQL使用查詢緩存,schema更新時緩存會失效,另外查詢緩存是依賴全局鎖保護,緩存大量數據時,若是查詢緩存失效,會致使表鎖死。

如圖8,以HBase爲例,讀取數據時,client先經過zookeeper定位到RowKey所在的RegionServer,讀取請求達到RegionServer後,由RegionServer來組織Scan的操做並最終歸併查詢結果返回數據。由於一次查詢操做可能包括多個RegionServer和多個Region,數據的查找是併發執行的且HBase的LRUBlockCache,數據的查詢不會出現所有鎖死的狀況。

圖8 HBase架構

基於新的存儲架構,咱們的數據維度從單局比賽擴展到了玩家、英雄、聯賽等。(如圖9)

圖9 數據維度

系統解耦

上文咱們提到1.0架構中使用In-Memory的消息隊列作數據傳遞,因爲內存中隊列數據沒有持久化存儲並與Master模塊強耦合,Master節點更新或者異常Panic後會致使數據丟失,且恢復時間冗長。所以,在2.0架構中採用了第三方的消息隊列做爲消息總線,保障系統「上下游」節點解耦,可屢次迭代更新,歷史消息變得可追蹤,基於雲平臺消息堆積也變得可視化(如圖10)。

圖10 數據監控

數據API層

1.0系統的數據API層爲實現快速上線,在架構上未作太多的設計與優化,採用域名的方式實現負載均衡,並使用開源的DreamFactory搭建的ORM層,利用其RESTful的接口作數據訪問。該架構在開發和使用過程當中遇到許多問題:

  1. API層部署在國內阿里雲上,數據訪問須要跨洋

  2. ORM層提供的API獲取表的全字段數據,數據粒度大

  3. 無緩存,應對大流量場景(如17年震中杯與ESL)常常出現服務不可用

  4. 多DB的數據聚合放在了API層,性能不足

  5. 服務更新維護成本高,每次更新須要從域名中先剔除機器

針對上述問題,咱們從兩個方面重構了1.0數據API層。(如圖11)

圖11 數據API新架構

鏈路的穩定性

全球鏈路上,咱們使用CDN動態加速保證訪問的穩定性。同時利用多雲廠商CDN作備份容災,作到秒級切換。

在調度能力和恢復能力上,咱們搭建了本身的灰度系統,將不一樣維度的數據請求調度到不一樣的數據API,減小不一樣維度數據請求量對系統的影響;藉助灰度系統,API服務更新的風險和異常時的影響面也被有效控制。依賴雲平臺可用區的特性,灰度系統也能方便地實現後端API服務跨可用區,作到物理架構上的容災。

另外,爲保證內部跨洋訪問鏈路的穩定性,咱們在阿里雲的北美機房搭建數據代理層,利用海外專線來提高訪問速度。

數據高可用性

接入分佈式存儲系統後,對外數據API層也根據擴展的數據維度進行拆分,由多個數據API對外提供服務,例如比賽數據和聯賽賽程等數據訪問量大,應該與英雄、我的及道具數據分開,防止比賽/賽事接口異常影響全部數據不可訪問。

針對熱點數據,內部Cache層會作定時寫入緩存的操做,數據的更新也會觸發Cache的重寫,保障賽事期間的數據可用。

結語

在本篇的技術分享中,咱們介紹了FunData數據平臺架構演進的過程,分析了舊系統在架構上的缺點,以及在新系統中經過什麼技術和架構手段來解決。FunData自4月10日上線以來,已有300多位技術開發者申請了API-KEY。咱們也在着力於新數據點的快速迭代開發,如聯賽統計數據,比賽實時數據等。

相關文章
相關標籤/搜索