案例:百度的評論系統是怎麼設計的?

百度評論中臺爲百度系產品提供便利接入、持續穩定的評論能力,是百度社區氛圍體系內最重要的基礎能力之一,日均流量達到百億規模,在業務不斷髮展過程中,百度評論中臺實現了功能快速迭代、性能穩步提升,本文將從整體介紹百度評論中臺的架構設計,同時結合具體案例講述如何構建高可用、高性能的分佈式服務。

背景

評論作爲用戶主動表達提供情緒 / 態度 / 觀點的重要方式之一, 其產品形態是以資訊作爲載體,建設生產 - 分發 - 瀏覽的用戶內容生態,最終促進內容與用戶,用戶與用戶,用戶與作者之間的價值交互氛圍。

在閱讀過一條資訊後,用戶往往會瀏覽到評論區,或是抒發內心情感,或是尋找一些共鳴,也可能只是滿足喫瓜羣衆的好奇心,有吸引力的評論區板塊可以滿足用戶的互動訴求,同時也可以增加用戶的產品粘性,促進業務發展;那麼如何構建一套評論系統,在業務上能夠支撐產品形態的多樣化以及持續的迭代,在性能上能夠提供穩定的能力輸出,下面我們將會詳細介紹百度評論中臺的架構設計思路以及在業務發展過程中的一些挑戰與探索。

概念介紹

評論從生產到讀取歷經多個環節,下圖是評論生命週期中各個環節的簡易模型:

評論數據的生產由用戶觸發,評論系統維護了與資源關聯的主題數據,通過主題信息來檢索歸屬的評論內容,評論數據本身維護了發表人,層級關係,是否展現狀態,內容,點贊數,回覆數,時間等等,每個數據維度用於特定的展現場景以及數據分析。

在用戶生產內容過程中,系統識別惡意請求,針對用戶、資源、內容等維度進行層層過濾,完成數據落盤,數據採用分佈式數據庫存儲,同時根據查詢維度進行垂直 / 水平拆庫拆表,以解決業務持續增長帶來的數據讀寫性能瓶頸,數據層面的變更最終落入大數據中心,用來分析業務現狀以及未來的發展發向。

服務設計

上文鋪墊了評論業務的相關概念,接下來我們側重於介紹整體的服務設計,評論服務最初立足於百度 App 業務,爲 Feed 等內容體系提供穩定便捷的評論能力,隨着公司在中臺方向上的戰略轉型,評論服務重新定位爲評論中臺,面向百度系的各個產品線,角色的轉變,引發了架構設計方面的巨大改變,同時也帶來了新的技術挑戰:

從業務接入角度來看,接入業務方多,需求量大,定製化要求高,需要中臺側對外能夠給予業務側友好的快速接入,對於此部分需求,我們作爲首箇中臺方完成了服務託管移動開發者中心(百度移動開發管理平臺)的接入,藉助該平臺管理接口權限、技術文檔、流量鑑權、服務計費以及售後諮詢等能力,大大提升了接入效率。

對於中臺側內部,通過建設統一的接入層網關,實現容災觸發、流量染色、流量控制、異常檢測等插件能力,既可以爲業務提供便利的技術支持,在中臺內部產生技術變更時,也可以做到業務無感知;

從能力輸出角度來看,不同產品線的業務特點對於基礎能力有着不同的訴求,這就需要中臺側對外能夠滿足業務需求,對內在架構設計中充分考慮通用化來保證有限的中臺人力的研發效率。基於此,我們將接口能力不斷抽象,在代碼層面上保證低耦合、高內聚,針對不同的產品需求設計組件能力,專項解決痛點問題,同時我們也嘗試針對特定的功能場景,提供友好的開發框架,通過與業務側開源共建的方式來提升需求上線的效率,解放一定的中臺人力,也取得了不錯的效果。

從系統性能角度來看,接入流量隨着新增產品線而日益增長,不斷考驗中臺服務的性能,我們需要承諾核心接口 SLA 視角 99.99% 的服務穩定性。從架構上看,去除單點服務,邏輯機房內部形成隔離域,無跨地域請求,考慮極端場景,設計技術方案防止緩存穿透,緩存擊穿,服務雪崩,保證服務的最小可用單元,從容量上看,保證高峯期容量滿足 N+1 的冗餘,具備單機房切空的能力,核心指標的報警覆蓋至運維 / 研發 / 測試同學等等;

總體來看,建設思路就是,服務分層治理,不斷沉澱中臺通用化能力,對於產品和技術的升級,不斷完善基礎建設。

挑戰與探索

上文以整體視角介紹了百度評論中臺的架構設計,下面我們枚舉了幾個業務發展迭代過程中遇到的具體問題,來詳細介紹下百度評論中臺是如何進行技術探索並實施落地的。

如何提升服務性能與迭代效率?

背景:

一個系統在歷經無數個版本迭代後,能夠承載的功能越來越豐富,同時也會面臨着開發成本日益增長的問題,業務邏輯夾雜着歷史包袱,代碼塊混亂冗餘,牽一髮而動全身,這個時候對系統本身的技術升級便勢在必行。

評論列表類接口是評論服務中的核心業務,歷史上大部分的功能迭代列表類接口都會參與其中,這也促使了列表類接口更快地暴露出問題。

從下圖優化前的業務邏輯處理流程不難看出,接口定義模糊,承載功能較多,相關的邏輯交織在一起,導致代碼可讀性不高;在業務處理部分,數據依賴比較多,不同的數據獲取之間還存在着依賴關係,服務調用只能順次執行,效率低下,這些問題會影響到日常開發的工作效率,並且在性能上也不可控,爲系統穩定埋下了隱患。

思路:

面對這一系列問題,我們對列表類服務進行了一次大刀闊斧的改革,採用三條路徑來解決核心問題:在接口層面,按功能拆分,定義單一職責的接口來解耦邏輯;在數據依賴層面,對下游調用進行調度管理,實現自動化,並行化;在數據渲染層面,採用流水線的裝飾器來定義業務主線邏輯與支線邏輯,整體做到優化後的服務接口功能專一,代碼邊界清晰,依賴服務調度管理,數據打包靈活,可持續拓展,那麼我們是如何實現這一過程的?下面介紹下具體的解決方案。

方案:

上圖是一個接口下發數據的整體流程,總結起來可分爲三個階段:

第一階段:作爲接口的前置條件校驗,對基礎參數進行校驗,過濾請求;

第二階段:依賴數據的集中獲取,這裏是服務優化的核心部分,這裏以評論一級列表接口爲例,一級評論列表接口的數據下發,包括了一級評論,二級外漏評論,評論總數,主態可見評論數據,標籤數據等等,每個數據源存在着一定的依賴關係。

比如,一級評論,主態可見評論,評論總數優先級最高,因爲不依賴其他數據源便可以直接請求,二級外漏評論則需要一級評論數據獲取到的父級評論列表作爲基礎依賴才能夠執行,這樣二級外漏評論的優先級次之,抽象來看,每個業務接口都可以梳理出自己的服務依賴關係,那麼接下來的問題就變成如何將這些依賴關係管理、調度起來,這裏我們採用了圖引擎調度模型來完成這個任務:

該模型實現過程分爲三個步驟:

第一步:定義,枚舉業務邏輯中所需要的所有依賴服務,並映射成節點,每個節點是數據依賴調用的原子化節點,該節點中不參與接口維度的業務邏輯;

第二步:定義他們之間的關聯,生成有向無環圖,每個節點維護其依賴節點的個數,用入度值來表示;有向無環圖的特點是節點與節點之間只有一個方向,不能成環,這種數據結構恰好符合業務節點之間的執行邏輯,把一個個服務依賴調用映射成有向無環圖中的節點,每個節點的入度值來表示該節點的依賴節點數,其中頂點就是圖中入度爲 0 的節點,沒有任何節點指向它;

第三步:由頂點開始,不斷併發執行所有入度值爲 0 的節點,上層節點彈出後,通過依賴關係尋找被依賴的節點,並對該節點的入度值做減操作,通過這種拓撲排序的結果輸出,實現自動化的節點運行;

總結來說,將各個接口的節點關係進行配置化管理,當流量請求時,讀取該接口的節點配置,生成有向無環圖,節點的任務由調度器統一管理執行,每個節點的依賴數據輸出會存儲到當前請求的調度器中,到下游節點任務執行時,從調度器獲取上游的數據結果作爲入參,完成本節點的執行邏輯;

第三階段:數據渲染階段,通過階段二獲取到所有依賴數據後,從調度器中獲取所有節點中的依賴數據結果,定義流水線式的格式化裝飾器,按功能拆分,對響應值進行層層修飾,以實現數據渲染的模版化處理。

通過這種方式改造系統後,接口的服務性能大大提升,平均響應耗時在 99 分位維度上有了明顯的降低,同時受益於 Go 語言的高性能,節省了物理機資源,重構後的代碼可維護性也大爲提升。

如何構建高性能、低延時的評論排序能力?

背景:

評論是生產用戶內容的服務,那麼對評論內容的運營則決定着整個評論區的氛圍,對於優質 / 有趣或帶有特殊屬性的評論內容應該得到更多的曝光率,同時我們也希望低俗 / 辱罵 / 無意義的評論內容得到更少的關注,這樣用戶之間的互動才能得到正向的循環,這就要求評論服務構建一套排序機制,在產品層面上能夠滿足排序的需求,在技術上也可以做到排序數據快速收斂,不影響服務性能的前提下快速迭代。

思路:

在制定技術方案之前,我們需要考慮到幾個核心問題,首先是如何對評論內容進行排序?

最容易想到的方案是爲每條評論評估一個分值,按照分值的大小來輸出一篇文章下的評論內容,以達到排序的效果;那麼既然每條評論有了評分屬性,接下來就要定義這個評分的公式,按照這個思路我們可以羅列出很多影響評論分值的因子。

比如一條評論的點贊數,回覆數,創建時間等等,同時爲這些因子設定相應的權重,組合起來可以計算出總分,在這個過程中,我們需要考慮公式的迭代給系統帶來的性能衝擊,當一個公式被確定下來,並不能馬上推送到線上,而是需要小流量來評估排序結果帶來的收益才能決定公式的因子或者因子權重是否是一組正確的組合,這便涉及到可能存在多組公式並存的情況,並且可以預料到公式的調整將會是頻繁的迭代。

根據這些思路,我們最終開發出評論排序框架,採用離線計算評論分值,公式配置化,扇出併發模型等技術手段實現了智能化評論排序功能,下面我們詳細看下架構設計。

方案:

評論排序服務分爲兩部分,離線粗排服務與在線精排干預。

首先介紹下離線粗排服務,我們將評估評論分值 / 輸出排序列表的任務放在離線模塊中執行,在數據量較大的情況下離線運算不會影響到線上服務。離線模塊會監聽評論行爲隊列,例如評論回覆,評論點贊,評論刪除,評論置頂等能夠影響評論列表數據變更的行爲,離線排序服務消費到行爲數據後通過策略公式計算出評論分值,最終存儲到排序索引中,這個流程涉及到的技術點如下:

離線粗排服務會針對評論的相關屬性輸出排序結果,在線部分,我們又對排序結果做了個性化的二次干預,包括個人評論提權,通訊錄 / 關注好友評論提權,以及精排模型干預,這些策略幫助我們更爲精準地定位用戶羣體的喜好,增加用戶瀏覽評論的共鳴感。

如何保證服務的持續穩定性?

背景:

隨着系統承載的業務流量越來越大,對服務穩定性建設方向上,評論團隊也投入較大的資源 / 人力佔比,一方面持續保證系統服務的高可用,不斷提升系統容錯能力,突發異常情況下能夠快速應對,保障業務的最小可用單元,另一方面在業務邏輯反覆迭代過程保證系統服務的高性能,不斷提升用戶體驗。

穩定性建設是個持續優化的過程,我們通過不斷探索,調研,選擇適合評論服務的技術方案並落地,評論業務特點是面向 C 端用戶,社會熱點事件會對業務產生直接影響,會引發讀寫流量的突增,其中寫流量的增加,對下游服務,下游存儲會產生不小的負荷,同時,過多的寫流量也會考驗讀流量的緩存命中率,一旦出現某一主依賴異常,整體服務將處於不可用的狀態,針對這種風險,通過我們在熱點,緩存,降級三個方面着手,爲服務的穩定性保駕護航,下面看下具體實現方案:

緩存管理:

對於讀流量的突增情況的處理方式通過設計多級緩存,提高緩存的命中率來處理,根據評論業務特點,我們設計了評論列表緩存,評論計數緩存以及評論內容緩存等,還使用了空緩存,來針對特定場景下大部分資源無評論的情況,防止緩存穿透。

在接受業務讀流量請求時,首先使用 LRU 的本地緩存抵擋一波流量,查看是否能在最熱最近的內存緩存列表中獲取結果,本地緩存並沒有命中,將會從 Redis 獲取緩存,如果是突發熱點,Redis 的命中率很低,流量回源到 db 依然有很大的風險,所以這裏使用了一層實例鎖,從實例維度控制併發量,只允許一條請求透傳到下游,其他請求等待結果,用這種方式來防止緩存擊穿。

熱點機制:

熱點事件往往會引發流量的突增,熱點事件產生時,通常會伴隨着 Push 類的通知,更加引發單篇資源的集中式讀寫請求,而且這種熱點事件產生時間比較隨機,很難做到提前預判,單篇資源讀寫流量的升高,會導致緩存命中率下降,甚至緩存徹底失效,大量請求直接打到數據庫,進而導致服務雪崩。

爲了避免熱點事件給系統帶來不可控的衝擊,我們設計了一套熱點自動發現 / 識別系統:通過消息隊列監聽資源維度產生的評論行爲,例如評論發表,回覆,點贊,審覈等等,通過計數來判定當前資源是否夠熱對評論行爲進行計數統計。

當一篇資源下的評論行爲持續增多,達到某一閥值(動態可配置)時,該資源被判定爲熱點資源,並將該資源推送至熱點配置中心,命中熱點的資源,其資源下產生的評論行爲將寫入熱點隊列,異步入庫,同時,寫操作後不再對緩存進行清理,而是重建緩存,來保證命中率。通過這一套機制,我們成功應對了一系列引發全民熱議的熱點事件,保證了用戶體驗。

容災降級:

當出現極端的故障時,對系統的穩定性會產生巨大的影響,比如下游服務承受不住突增的業務流量,請求超時,實例問題引發的單點故障,機房網絡問題導致不可用等等,我們的服務每日承載着百億級別的 PV 流量,幾秒鐘的服務不可用就會產生巨大的損失。

因此容災降級能力是考驗系統服務高可用的一個重要標準,我們在這方面做了一系列的措施來提升系統應對風險的能力,建設業務在異常狀態下的最小可用單元。

在容災機制上,容災觸發細分爲主動與被動,當業務與到可用性毛刺抖動時,由接入層進行感知,自動進入被動容災邏輯,寫請求進入隊列異步入庫,讀請求直接返回容災數據,提升容災數據使用率。

主動容災與運維平臺打通,實現按接口、按機房、按比例來判定服務是否降級成容災狀態;在依賴管理方面,梳理業務弱依賴,一旦發生某一依賴服務異常,可以直接對依賴進行摘除。

在混沌工程方面,爲了能夠應對線上各種突發狀況,對服務進行故障設計、故障注入,並針對服務給予的反饋,制定相應的預案建設,並將故障演練例行化,定期檢驗系統能夠承受的風險級別。

總結

百度評論中臺發展至今,歷經了角色定位 / 技術架構的轉變與升級,不斷探索應用創新,打造極致的用戶體驗,目前,評論服務爲百度系 20 + 的產品提供評論功能,峯值 QPS 達到 40w+,日均 PV 達到百億規模,同時能夠保證 SLA 視角的接口穩定性在 99.995% 以上。在未來的發展規劃中,百度評論中臺在服務創新、中臺建設、穩定性等方面還會繼續深造,助力建設優質的百度社區氛圍。

分佈式實驗室 關注分佈式相關的開源項目和基礎架構,致力於分析並報道這些新技術是如何以及將會怎樣影響企業的軟件構建方式。

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