愛奇藝基礎數據平臺演進

愛奇藝基礎數據平臺主要是爲了統一公司內部的基礎數據交換規範,解決不同團隊之間 ID 不統一問題(各團隊都有自己獨立的 ID)、數據定義不統一、數據更新不及時等問題。

隨着公司業務發展,除了視頻基礎數據,還逐步對接了 UGC 視頻、全網影視資料、資源位、直播、遊戲、文學、電商等公司大部分業務方的基礎數據,支持海量業務數據的存儲、分發、在線查詢、離線分析等服務。

目前已有近百張數據表,總數據量數十億,數據日增長百萬級,日消息量千萬級,覆蓋公司幾十個業務團隊。

本文將從愛奇藝數據平臺在實際業務中解決 HBase 高可用、消息服務高可用以及平臺整體服務水平擴展能力等方面,敘述愛奇藝的探索和實踐。

服務能力

愛奇藝基礎數據平臺主要提供以下能力:

整體架構

服務流程

首先需要通過統一管理平臺,定義好表及其的字段類型結構,隨後會發布基於 Protobuf 的數據定義包,通過這個包來使用分發平臺中存儲的數據。

生產業務通過 ID 服務、寫入服務將數據寫入平臺,平臺會先將數據入庫 HBase,後會發送一個更新通知消息,下游業務通過訂閱消息獲取到具體變更的 ID 及字段信息,再通過讀取服務獲取該 ID 的最新數據。

平臺內部也基於消息將本次變動的內容記錄於 HBase,方便業務排查定位問題,尤其是數據結果不對時,業務可以很快通過這個變更記錄查詢到是哪個業務在什麼時間具體 IP 地址改動的數據,在實際工作場景中使用頻率較高。

對於消息合併服務主要是對寫入觸發的消息進行相同 ID 寫入的合併,減少發出的消息量,降低下游訂閱業務的處理壓力。我們針對消息區分了優先級,不同的優先級有不同的合併窗口時間,例如:直播等業務對時效性相對敏感,消息合併則窗口期更短。

服務方案

4.1 ID 服務的高可用

ID 服務使用 2 個 MySQL 集羣,其中一個 MySQL 示例只生成單數 ID,另外一個 MySQL 生成雙數 ID,這樣可以做到其中一個 MySQL 不可用時,另外一個 MySQL 可以正常提供服務。

4.2  消息分發

平臺本身存儲了很多不同業務的數據表(例如:視頻、直播、圖書等),業務可以根據自己的業務需要可以訂閱單個或多個不同的業務表消息並做一定規則的過濾,而這種場景屬於比較普遍的,所以由平臺本身實現比較合理,不用每個業務都做一遍。基於這種背景下我們最初使用了 ActiveMQ 的 VirtualTopic 做了大類的區分,但一段時間後我們發現這種方式並不夠靈活,無法控制的更精細。爲此我們自研了一個 ActiveMQ 插件來滿足相對精細的消息分發控制,整體結構如下圖:

通過管理平臺我們將規則通過一個特殊的 Topic 推送到插件,插件本身會監聽這個 Topic 消息,將規則保存在內存中並持久化,插件會在每一條消息發送之前對消息進行一個路由,根據訂閱規則匹配發送到 1 個或多個隊列中,原理類似 AOP 機制。

問題及解決方案

5.1  HBase 讀取性能差的問題

由於本身平臺業務場景決定,一次寫入對應 N 次讀取,所以在極端場景下,線上偶爾發生過 HBase 某個 RegionServer 宕機的情況,進而造成大量的超時情況。

目前我們的主要解決的思路就是加緩存,讀多寫少就是緩存的主要場景。在數據庫選型上,我們在 Redis、CouchBase、MongoDB 上進行了調研,最終選擇了 MongoDB,主要的原因是 Redis 和 CouchBase 在容量上不滿足業務需求。在我們對 MongoDB 的壓測中,性能方面也在可接受範圍內。

緩存方案如下:

寫入服務每次請求都會生成一個唯一的 SessionID,我們將這個 ID 作爲數據的版本號,緩存是否失效使用這個版本號來判斷。每次寫入時更新緩存以及讀取時緩存失效時更新緩存都爲異步,主要是爲了降低延時,以及避免緩存更新失敗導致寫入失敗。

爲了保證緩存和 HBase 的一致性,每次請求都要讀取 HBase 中存儲的版本號,這也對 HBase 造成了較大的壓力,爲了解決這個問題,我們將 HBase 中 SessionId 設爲單獨列族,並設置 IN_MEMORY => ‘true’來優化。      

5.2  HBase 可用性

由於全部數據都存儲在 HBase,所以提升 HBase 本身的可用性就尤爲重要,目前單集羣內的單節點故障,HBase 本身的機制是可以保證的。但是如果整個集羣故障或者集羣所在機房出現了故障,如何能保證服務可用?

經過調研目前開源版本 HBase 還沒有相對完善的跨機房部署方案,例如單個機房故障情況下不影響服務正常使用。

我們在結合服務特性的情況下設計的 HBase 同城主備高可用方案,如下:

Mongo 作爲寫入緩存,保存 WAL(WriteAhead Log)Mongo 三機房部署,高可用。

Synchronizer 服務將 WAL 寫入主 HBase,異步服務。

主 HBase 與備用 HBase 建立數據同步。

寫入流程:Write 服務只寫入 Mongo,由 Synchronizer 同步服務將數據同步到主 HBase。

讀取流程:同時讀取 Mongo 和 HBase,將 WAL 最新數據與 HBase 數據合併得到最新數據返回,讀取服務使用 Hystrix 進行熔斷,如果主 HBase 宕了,Mongo 中數據與備用 HBase 集羣仍然可以合併出最新數據返回。

Mongo 中 WAL 設置 TTL,時間大於主庫到從庫的同步延遲。

目前該方案已經在生產環境經歷了 2 次故障,並且故障對讀寫無影響,上下游業務無感知。

5.3  ActiveMQ 碰到的問題

對於第一個問題我們的處理這種問題的方式也比較簡單粗暴,通過之前開發的插件對每一個隊列進行閾值控制,超過一定閾值則不再繼續發送消息並通知業務及時消費消息,這對業務本身是有一定影響的,對業務不友好,治標不治本。

在經歷了較多次線上問題後,我們決定考慮其他消息中間件,在調研了市面上主流的消息中間件後我們將 Kafka 和 RocketMQ 作爲備選,在選型的時候我們主要考慮幾個因素,可用性、可靠性、水平擴展能力,在這 3 項中兩個中間件都滿足需求。

還有一個需要考慮的因素就是消息過濾或分發,因爲存量隊列都有訂閱規則,考慮業務遷移成本問題,這個訂閱規則實現還是由平臺實現,對比發現 RocketMQ 支持在服務器端過濾,這個特點吸引了我們,在經過功能驗證後,該功能滿足需求,最終選型 RocketMQ。

部署方案如下:

單集羣同城 3 機房部署,主從部署在不同機房,保證單節點宕機、單機房不可用消息發送和消息消費不受影響,並且消息消費的時效性也不受影響。

我們還開發了基於 RocketMQ 客戶端的 SDK,過濾規則都存儲在配置服務,由 SDK 負責將訂閱規則推送到 FilterServer,業務可以更簡單的遷移到 RocketMQ,消息過濾在集羣端,所以效率更高,可以減少不必要的消息投遞到客戶端。上線後,徹底解決了之前 ActiveMQ 的問題。

5.4  擴展讀能力

隨着業務的不斷髮展,對接的業務會越來越多,現有讀寫邏輯相對複雜,讀取能力並不能完全達到水平擴展的能力。爲了可以更好支撐未來的業務發展,需要進一步提升讀取能力,使服務讀取能力完全做到水平擴展。在數據庫層面,通常有讀寫分離,也就是正常的讀請求操作主庫,其餘純讀取的請求使用從庫來解決這種問題,但是由於業務場景限制,很多業務都是通過訂閱以及獲取最新數據的方式來同步平臺數據經過一定的業務處理、抽取、加工成最終業務想要的數據,所以單純將所有用戶的讀取請求都轉爲讀從庫顯然並不合適,而且這些請求中還有一部分讀請求是寫入業務的先讀後寫,但是這種方案也給了我們啓發。

由於平臺業務較複雜,無法單純在數據庫層面做讀寫分離,所以就新增一個業務層面的從庫,通過業務服務同步主庫數據,這樣下游業務可以通過從庫同步或者單純讀取數據,而且從庫可以增加多個。

服務方案如下:

新服務 SlaveRead(從庫)可通過消息 + 讀取的方式從主庫同步最新數據,更新後發出消息,提供給下游服務使用,業務可以以相同的方式在從庫同步數據或者單純的讀取請求。

從庫與從庫之間可以建立同步關係,這樣整體同步的壓力就不會壓在主庫上,避免從庫多進而增加主庫壓力,最終實現了平臺能力可水平擴展。

總結

總體來說愛奇藝基礎數據平臺通過在技術和服務方案上的不斷改進解決業務實際碰到的問題,在 RocketMQ、HBase 上也積累了一些實戰經驗。未來將繼續探索提高平臺整體服務能力、服務穩定性、性能等方面的技術及方案。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/zKZ8jJ1pzNlZ2s3jpeFvJA