騰訊內容平台系統的架構實踐

騰訊內容平臺系統的架構實踐

隨著雲結合微服務架構切實的提高了生產效率;深度學習不斷深入內容處理的各個領域促進生產力的發展。 在消息系統,數據倉庫,計算框架,存儲系統等基礎架構層建設逐步提升的基礎上,大型互聯網公司進一步提出了業務基礎設施的需求。在基礎架構和上層業務之間急需一箇中臺系統來承載。中臺系統把業務層同性的算法能力,服務能力,業務能力高度集成,有效組織 ,動態規劃。更好的幫助上層業務。

工程篇

前身

在 15 年的時候,內容平臺(承載騰訊包括手 Q 等內容業務等的內容中臺)最初來源於 QQ 公眾號系統(公眾號系統承載了包括 QQ 服務號,訂閱號的關注關係,紅包等大型活動的推送,訂閱號消息下發,素材內容管理等)。當時公眾號系統有幾個子系統:資料子系統,消息子系統,關係鏈子系統 和 素材子系統。一個號主如果需要把自己的內容給粉絲,需要經過這 4 個子系統。


騰訊內容平臺系統的架構實踐


(藍色部分)

(子系統就是有獨立的存儲 邏輯 數據流 接口體系。概念來自 Systems Analysis & Design 的 DFD sub-system)

在最簡單的粉絲髮送的場景,首先使用素材系統管理群發任務的內容,然後用關係鏈子系統拉取粉絲數據,通過消息子系統創建群發任務進行消息發送,過程中需要和資料子系統交互獲得各自參數。

在 15 年下半年,內容戰略升級,除了來自平臺的號主發送內容,我們還有大量來自其他外部合作平臺。他們通過公司其他平臺對接進來,當時我們複用這套基於消息發送的場景,讓對方創建群發任務,內容進入素材庫進行處理,然後就可以觸達粉絲了。

但是後來整個業務形態從訂閱變成了 Feeds 流,原來的粉絲關係變成了推薦,隨著內容處理服務的越來越多,內容量的不斷增加,老的這套系統就無法承載了。於是我們需要改造老的系統。

我們希望是有一套統一的多源內容庫,在良好的擴展性框架下,各類型服務通過實現預定義接口,完成對內容的加工處理,人機結合,輸出給訂閱方。

內容的處理服務包括了內容安全質量(質量評價,暴力色情,低俗,標題黨,錯別字等),內容建模特徵(分類,主題,標籤等),內容理解生成(封面圖,摘要,結構化,剪輯等)。

騰訊內容平臺系統的架構實踐


(內容平臺頂層圖)

文本介紹一下其中主要的架構部分工作。

存儲

物理存儲

原來存號主發送內容的素材系統就變成了內容平臺的最早雛形,素材還是通過 MySql 來存儲用戶發送的內容,所有的文本和 html 生成的頁面樣式也存儲在一張表中,單表不堪重負,進行了 partition Sql 執行優化等工作,但是無濟於事。在進入內容時代我們需要有一個性能更高的存儲系統來支持。 當時的技術選項的考慮基於過去素材系統的痛點和未來需要支持的規模。

  1. 我們需要我們的存儲系統能支持任意的字段擴展 ,Schema Free。便於擴展根據列的定位效率需要在 O(1)
  2. 存儲系統一定要支持永久存儲,同時能滿足基礎的併發讀,雖然不要求像 Redis 一樣上萬 /s ,至少也要是千級別。
  3. 需要支持多機 水平擴展。
  4. 公司有團隊成熟運維。

當時考慮過 Mongodb 和 Hbase ,Cassandra 以及其他 KV 存儲。

Mongodb 的好處很多 。但是他的高效率訪問帶來的是內存資源的極大開銷。冷熱不均的分配,不可控的併發寫入和副本存儲都使得他無法承載未來幾年更大的發展。

其他的就是 Hbase ,當初能預料到的是如果我們需要把 Hbase 當作 KV 對在線服務,是無法承受的,但是我們可以在其之上增加一個 KV 的 cache 解決這個問題,剩下的事情就是我們去打造一箇中間層支持 Hbase 和內存 KV 的數據同步。

Hbase 的 row key + column family + column qualifier + timestamp + value 是 HFile 中數據排列依據。HFile 據此,對數據的索引到 data block 級別,而不是行級別。

另外當初還考慮過一個方案就是基於 LevelDB 的全新內容中間件方案,這樣能做到內存 KV 和永久存儲合二為一,可是在那個時候的環境下,我們就算之前做過初級版本,也無法快速開發來,但是 Hbase 的好處是他可以支撐一段時間的 KV 訪問,未來扛不住再優化上增加 Cache,事實上後來我們也是這樣走的。

關於存儲這裡的工作我們後面還會提到,我們怎麼進化到存儲中間件 RCS。存儲有了接下來就是如何設計存儲層的數據模型。

數據建模

在設計存儲模型的時候,在 16 年的時候 ,確認的事情有幾個:

  1. 內容處理的肯定會有大量的模塊並行的需要對內容進行處理加工;
  2. 這些模塊有共性的屬性獲取,也有特化的屬性獲取需求。
  3. 模塊自身彼此會產生輸出給其他模塊用。

我們的目標:

在架構上,打造統一存儲來託管所有模塊需要讀寫存儲的場景,這樣每個模塊的同學統一存儲。無論是業務同學的業務邏輯字段。還是算法同學的模型業務輸出 or 模型特徵輸出。開發人員需要更加關注於策略本身,存儲上的事情統一收攏提供 API 就行。

在表結構上:

如果第 1 點做到了,那麼我們未來可以基於這個寬表進行天然的單表檢索,單表基礎內容特徵挖掘。甚至是算法實驗字段都可以統一在一張寬表裡。

於是我們做了幾個重要的設計:

1、推廣新的唯一 ID 體系,廢除公眾號的自增 articleID,ID 能支持以下特徵:

ID 體系 = 預留字段 + 時間 + 自增 ID + 內容類型 + 業務來源

於是有了 rowkey,拿到任意一個 rowkey 我們至少能第一眼知道來源大概的時間和類型,便於路由。

2、規範列名,所有列名分為【狀態類】和【內容特徵類】,前者用於標記狀態,處理情況。後者用於保存內容的基礎元信息,模塊處理過程中產生的結果信息,中間信息。當時列的結構約定的格式是:

列名 = 列屬性(狀態類 or 業務類 or 模型類) + 字段屬主 + 字段描述


騰訊內容平臺系統的架構實踐


狀態類

騰訊內容平臺系統的架構實踐


模型類

當時所不能預估的事情,現在思考有幾點:

  1. 業務字段可能會根據不同的業務場景產生「多態」,這個在語言中很好解決的問題,落到存儲層會有不少問題。 業務場景之間,多個業務之間對同一個內容的標題和封面圖都可能有自己的子類,需要增加場景概念。
  2. 當初假設的是執行是樹或者圖這樣的深度遍歷 DAG,不會產生迴路重遍歷,事實上居然真的出現了這種場景。
  3. 隨著字段的成倍擴張字段,列名一直沒有很好的規劃收攏分配,造成開發人員組織架構複雜之後不可控,需要有個合理的收攏分配方式。

我們在這裡的數據模型使用了寬表格式,相比複雜的 EVA 存儲,我們覺得寬表更加利於數據彙總統計。後續 RCS 部分會再次介紹。

寬表事務性更好。HBase 對一行的寫入(Put)是有事務原子性的,一行的所有列要麼全部寫入成功,要麼全部沒有寫入。但是多行的更新之間沒有事務性保證。

線上當前真實的情況是,單文章表已經有超過 500 多稀疏 column ,並且隨和業務場景增加不斷增加。測試數據驗證並不會隨著 column 增加而影響查找開銷。

相關對比可閱讀:

https://stackoverflow.com/questions/16447903/table-design-wide-table-vs-columns-as-properties


騰訊內容平臺系統的架構實踐


過程建模

主要介紹服務的設計區分,這裡的服務包括業務能力服務,比如轉碼,業務邏輯處理,也包括模型算法相的服務。還有數據流的架構方法。

服務分層架構

我們把任何一個服務分層L0-L2三層,0層就是原子化的,不適合業務場景的無狀態基礎能力,如果在 NLP 的應用上,類似下圖:

騰訊內容平臺系統的架構實踐

騰訊內容平臺系統的架構實踐

類似 kernel 支持 IO_DIRECT ,VFS 提供直接訪問操作塊設備層。我們的模型服務也可以支持直接越過業務邏輯訪問底層基礎能力。

(具體 linux io 相關參考 http://km.oa.com/group/17746/articles/show/208771)


騰訊內容平臺系統的架構實踐


從開發任何一個服務的時候,我們先梳理好層次關係。[對外的協議和接口]這裡可以統一代碼框架,由純工程人員提供,包括並行化雲化容災過載保護熔斷等。

業務測的算法團隊使用其他公共庫的 L0 or L1 層的能力,進行 L1-L2層的業務開發,工程人員也可以一起進行 L2 層的開發。


騰訊內容平臺系統的架構實踐


(服務模塊內分層示意圖)

我們根據以下幾個維度可以來區分一個服務,下面提到的調度中心根據這些維度進行不同的處理策略。


騰訊內容平臺系統的架構實踐


是否需要全局的信息:

指的是針對一個內容的處理,是否需要歷史上所有已經處理的內容的輸出。比如去重,或者同質化內容控制,一定需要和歷史上已經積累的內容處理狀態做邏輯。增量 + 全量同步。

是否強業務邏輯

所謂強的業務邏輯 就是說這個服務是否和業務邏輯沒有強關聯,可以獨立於業務場景。如封面圖抽取。

不一樣的有如:同質化的內容控制,這個和業務策略強相關,不同的業務場景 ,策略有存在特化的可能性,類似文件系統的實現有 ext4 xfs。同樣標籤抽取服務,也會對不同的垂類有不同的業務策略。

是否可以服務化

一般來說服務本身的業務邏輯越弱,又不依賴於其他業務全局信息的,可以提供對第三方的統一服務,比如圖片標籤抽取, 標題黨。

這類服務可以充分輸出服務自身的信息提供給上層業務方進行使用。

是否可以異步處理

是否能異步取決於,服務處理的時間效率,如果處理時間能保證實時返回那異步的需求不大,有的服務在特定業務場景下是嚴格依賴的,比如封面圖,一定是需要處理完成才能繼續。有的服務是可以在內容上線之後在執行並且更新,例如同質化控制,可以先曝光再下架。

是否有前置依賴性

如果有前置依賴的服務,一定需要在前置的若干服務處理完成之後才能進行本服務。

比如視頻拍重,一定依賴之前的視頻抽 frame,音頻指紋服務都完成之後,才能繼續進行。該服務需要任務圖中依賴前置服務父節點都完成之後啟動。

我們認為當開發一個服務之前,必須搞清楚這裡的幾個維度。比如我們現在需要引入 xLab 的標籤能力,我們綜合看這幾個維度,


騰訊內容平臺系統的架構實踐


過去我們有幾個思路,服務是否需要深入業務場景,那這裡的問題就是,如果和模型能力強相關的業務邏輯,是需要放在服務自身實現的,因為這樣可以更加有利於業務邏輯和算法輸出相互配合,形成整體,對於開發人員來說,理解業務的需求,是業務部門的算法人員必須關注的,比如標籤,去重, 模塊內部算法特徵使用和業務邏輯高度內聚。

流程處理

Brendan(也是 Kubernetes 的作者,現在微軟,之前在谷歌 Cloud)在 18 年的新書 Designing Distribution System 中提到了我們從 15 年開始嘗試的幾個路徑, 幾乎做了類似的模式和事情和經驗總結,感慨的是這本書 18 年才出版。 (道路自信 理論自信 文化自信 ☺)

Brendan 稱這種多階段多模塊協作的處理方式為 Batch Computational Patterns . 包括 Work Queue Systems ,Event-Driven Batch Processing, Coordinated Batch Processing。


騰訊內容平臺系統的架構實踐


(Event-Driven Batch Processing workflow 示意圖)

騰訊內容平臺系統的架構實踐


(內容中心簡化版的視頻處理 workflow 示意圖)

我們的處理框架經歷了幾個階段。

批處理模式(2016):

開始時完全的去中心化的思路。當時背景是時間緊,需要構建的模塊多。在 16 年,團隊不到十人的情況下,基情滿滿的開發了接近一百多個模塊,出入庫,圖文的去重,封面圖,外部能力對接,一些早期質量模型,視頻視頻轉碼,爬蟲等,同時還在兼顧老的公眾號系統的開發工作。

內部: 這個時候所有的事情都需要快速往前,模塊與模塊之間通過存儲的狀態變更進行交互,開發同學高度自治負責模型能力和業務場景,每個服務功能單元內高內聚低耦合。 依賴掃描存儲的狀態字段監聽狀態,生產變更。

外部: 16 年中旬,因為下游業務的增多,彼此交互需要松耦合,單模塊可分配的處理時間不斷壓縮, 具備基礎的多生產,多消費模式。我們引入消息隊列機制來對接多個下游業務方 CMS ,推薦等。通知內容的出入庫,狀態變更。

事件驅動批處理模式(2017):

在 16 年那個時候有任務調度和 討論所有模塊以後都預留管理接口,能接受緊急情況下的調度。也考慮過直接改造增加消息隊列的方式。到了 17 年初熱點內容對內容處理提出了很高的時效性,我們當時幾位同學反覆討論是否需要有中心化的調度服務來進行觸發和扭轉。產生了以下幾種方案:


騰訊內容平臺系統的架構實踐


方案 1.1 的優勢是:

全局無中心模塊。任何模塊都可以在故障情況下進行自我處理。

模塊之間松耦合通過消息隊列彼此通信。


騰訊內容平臺系統的架構實踐


(1.1 模型)

當時我們在 1.1 和 2.0 兩套方案中有過很大的討論。

目標是支持事件驅動,但是到底繼續延續無主的消息隊列方式(拓撲配置可以通過配置中心管理)還是把服務的觸發邏輯收攏到調度中心來控制,其實是一個技術導向和業務導向的問題。最終我們選擇了業務。

我們對調度中心的定位:

調度中心 = 事件驅動器(拓撲管理)+ 流量控制 + 容災 + 監控(性能,調用鏈跟蹤)

方案 2.0 的優勢是:

  1. 業務模塊之間沒有處理的時序依賴。
  2. 調度中心可以根據結合容量模型,動態調整模塊併發處理請求的數目。
  3. 調度中心可根據來源維持多個拓撲,起到配置服務器的作用。


騰訊內容平臺系統的架構實踐


(2.0 的調度中心 右下角)

我們選擇了 2.0 一個良好的調度框架,可以和存儲層結合,服務自我註冊。

通用的框架

我們可以抽象和定義了一批操作子,使得調度服務可以在這些操作,並且和底層的 RCS 存儲結合起來。上層界面可以直接拖拽 各種服務,並且確定操作子,可視化的構建 workflow。

Filter:

對於經過的內容進行強制過濾處理,一般用於基礎安全過濾和內容高準確率下的去重,降低入庫被攻擊的風險。

Copier:

支持一份內容 fork 成多個場景內容,繼承已有字段。這樣可以對一個內容的處理後分拆成多份對等繼續接下來的處理。在存儲層就產生了多條具有同樣外鍵指向的父記錄的記錄。

Divider:

多路劃分,根據存儲層已經產生的通用字段,對業務的處理進行分支分流的邏輯。比如當對於低於多少分質量分的號主發文。分高質量和低質量兩路進行不同處理(是否灰度,是否審核等)。

調度使用各個服務實現的模版模式操作服務接口,外部服務可以結合適配器模式保證接口統一。通過抽象這類操作讓存儲天然和調度和服務框架能互相結合。


騰訊內容平臺系統的架構實踐


(workflow 和服務構建的示意圖)

擴展

在 workflow 的設計中有 2 個主要陣營,管絃編排方式 Orchestration 和 舞蹈編排方式 Choreography。


騰訊內容平臺系統的架構實踐


參考:

https://wso2.com/blogs/thesource/2016/03/orchestration-and-choreography-when-to-use-an-esb-vs-a-workflow-engine/

前者注重中心化的調度服務,便於統一控制所有業務邏輯,便於監控及時干預。壞處是,耦合度過高,變得臃腫,而各個服務退化為單純能力模型,容易失去自身價值。

後者純事件驅動各個監聽改事件的服務,會主動獲取事件處理,並可以按需發佈自己的消息。優點是低耦合高內聚,技術人員充分自主。壞處是,業務流程是通過訂閱的方式來體現的,需要增加額外的調用鏈跟蹤框架做監控,且可能有服務掉鏈子 Process interruption 還是需要額外介入。

我們的 2.0 模式吸收兩者優點,調度中心起到事件發生器的作用,在規定的操作原語的場景下進行流程邏輯編排,同時各個服務自身保留了 L2 層的業務邏輯,避免出現 coordinates 業務複雜化。

關於調度

每次我們同學在過公司技術評審的時候都會遇到一個問題,你們為什麼不用 storm。這裡其實是這樣,Storm 本身其實是一套實時數據流式的調度框架,並不包含業務邏輯,類似 Torque 這樣的作業調度。用戶需要在 Topology 中編寫自己的調度邏輯。 Spout 和 bolt 單元需要用戶在固定的框架下面進行數據流的交互。

這基本侷限了我們的自主空間:

  1. 增加修改 blot 要重啟 topology,不能熱更新。
  2. 適合數據的實時統計,而不是多維度業務對象的算法邏輯處理,不適用自定義事物原語的場景。
  3. 系統運維相比自研框架會更加曲折,後者對算法模型的同學僅需要規範協議,且 storm 語言主要還是支持 Java。
  4. 對於上面提到的異步返回的服務沒有天然的支持。

內容數據流

說完 Workflow, 我們再來看看 Dataflow。在劍橋 Martin Kleppmann(分佈式系統專家,也是另外一本 Stream Processing 書的作者)的神作:Designing Data-Intensive Application 一書中,Martin 提到數據流系統把數據(我們這裡指內容數據)從一個服務發送到另外一個服務有幾種方式:


騰訊內容平臺系統的架構實踐


(原書截圖)

在軟件工程裡所有的數據流動都要設計好數據流圖(Data Flow Diagram)

內容訂閱

我們內部服務之間的早期數據流通共享,就是 Model_1 基於 Databases ,我們通過字段的 column 區分,嚴格時序,保證數據的一致性。

後期隨著 Workflow 2.0 改造,我們對於寬表的模型字段和業務字段分開訪問。在調度中心和服務之間之間對於寬表的業務字段,統一通過 Model_2 接口傳遞的方式。

在系統與系統之間 (下圖虛線框),我們通過 Model_3 的消息隊列總線的方式。對外統一通過消息隊列總線的方式進行同步,數據包括增量和全量,採用多狀態的版本控制,不同的處理狀態使用不同的版本號。同時隨著業務一致性等場景要求,外部也可以通過我們提供的數據中間件服務 RCS 讀寫業務數據即 Model_3。


騰訊內容平臺系統的架構實踐


(內容平臺 Dataflow 示意圖)

內容中間件

到了 17 年下半年,我們發現隨著需要數據同步的業務場景逐漸變多,多個業務場景之間的內容數據出現了不一致。同時多個業務會有直接訪問底層數據庫的需求。於是將一套存儲搭配多業務場景副本,變成了一套存儲,統一支持多業務場景訪問,同時合併了之前對外同步的 Model_2 消息傳遞方式,打包進一個組件,也就是上圖的 RCS。

存儲的數據中間件,同樣分為 L0,L1,L2 三層,

L0:Cproxy 專注於對底層存儲的訪問數據層異地容災。

L1:Sproxy 用於存儲介質訪問路由,OR-Mapping 對外統一場景字段,比如一個 cover 可能根據策略的不同,mapping 到不同模型實驗組的 cover column,對外屏蔽的。

L2 最上層 Eproxy 用於整個業務層的邏輯控制。


騰訊內容平臺系統的架構實踐


(RCS 組件,三層 proxy 對應 L0,L1,L2)

(HBase 本身的 replication 會拖慢主庫的寫入速度,數據又是我們的核心資產,我們在 17 年開始增加了 proxy 寫入異地冷備,並且定期和架平進行切換演習)

關於 RCS 的細節我們後續再另起展開,有幾點需要提一下為什麼,他能稱之為存儲中間件。

收攏所有對存儲層的操作行為。

支持任意字段的直接訪問,並且加了權限控制 。

支持通過內置的 MQ 對接外部實時數據統計服務,通知流水,更新數據操作。

比 Hbase 的 replication 更好的副本機制。


騰訊內容平臺系統的架構實踐


類似的工作: 小米在 2018 年 9 月發佈了他們的開源組件 Pegasus 1.11.0。

內容檢索

我們做了一個工作讓 RCS 的數據能實時同步到 ES,通過 ELK 進行多維度檢索,趨勢巡檢。同步到 Hive 進行全量的內容挖掘等。有了寬表,我們很容易執行如下命令:

稀缺度實驗 B 挖掘出來的某個時間段內封面圖評分>8 分並且標籤數少於 3 個的視頻總數。

騰訊內容平臺系統的架構實踐

騰訊內容平臺系統的架構實踐

(基於 ELK 的監控視圖)

寫在最後

感謝過去曾為這套系統付出的工程開發同學 alexcxu carrickliu ericxjiang guangyupeng jianxunzou jamescxchen mamoyang maplechang marcopeng taoyang tedqian xiaoccwang yuliangshen 以及能力模型同學 chenchwang lshzhang haodeye leafxin louislwang loopingwang jordanyu tiantianfan yaoyaoyu yurunshen vincentqliu 還有 AI 特徵工程師 dongdong 和勇哥。

孫子荀,(騰訊/SNG內容平臺部/平臺產品中心/算法平臺和後臺一組組長),11 年在百度從事高性能算法方面的工作。12 年加入騰訊,15 年開始負責QQ 公眾號平臺和內容中心後臺,並和團隊從無到有一起搭建了QQ的內容平臺。16 年開始從事內容處理能力的算法研究和落地工作。


分享到:


相關文章: