微信讀書後臺架構演進之路
今年是微信讀書上線 10 週年,後臺架構也伴隨着微信讀書的成長經歷了多次迭代與升級。每一次的組件升級與架構突破,在一個運行了 10 年的系統上落地都不是一件容易的事情,需要破釜沉舟的決心與膽大心細的業務聯動。本文簡要概述微信讀書後臺團隊近幾年的一些架構迭代以及背後的思考。
前言
今年是微信讀書上線 10 週年,後臺架構也伴隨着微信讀書的成長經歷了多次迭代與升級。每一次的組件升級與架構突破,在一個運行了 10 年的系統上落地都不是一件容易的事情,需要破釜沉舟的決心與膽大心細的業務聯動。
微信讀書經過了多年的發展,贏得了良好的用戶口碑,後臺系統的服務質量直接影響着用戶的體驗。團隊多年來始終保持着 “小而美” 的基因,快速試錯與迭代成爲常態。後臺團隊在日常業務開發的同時,需要主動尋求更多架構上的突破,提升後臺服務的可用性、擴展性,以不斷適應業務與團隊的變化。
整體架構
微信讀書是獨立於微信的 App,且由於歷史原因,開發及運維環境均存在一定的差異與隔離。因此,微信讀書的後臺服務實現了從接入層到存儲層的一整套完整架構。
架構上分解爲典型的接入層、邏輯層和存儲層:
-
接入層按業務劃分爲多個 CGI 服務,實現了資源隔離。在 CGI 層面還實現瞭如路由、頻控、接入層緩存、長連接等
-
邏輯層採用 WRMesh 框架構建了多個微服務,這些微服務按業務場景進行劃分,實現了不同模塊間的解耦。框架也提供瞭如 RPC、路由發現、過載保護、限流頻控、監控上報等能力
-
存儲層主要採用 PaxosStore 存儲用戶數據,分爲 K-V 和 K-Table 兩種類型,具備高可用、強一致的特性,針對 String 和 Table 兩種類型定製了緩存中間件,以適配某些業務場景下對訪問存儲的性能要求。BookStore 提供書籍的存儲服務,滿足讀書場景下對書籍的拆章、修改、下載等需要。此外,也不同程度地使用了騰訊雲的 PaaS 存儲服務,以靈活滿足更多場景需要。
具體的業務邏輯不再贅述,下面簡單介紹下微信讀書近幾年在後臺架構上的一些演進。
RPC 框架
微信讀書後臺微服務源於 Hikit 框架,採用 C++ 開發。該框架誕生於廣研、QQ 郵箱年代,在性能、容災、運維、監控層面都經受了線上的考驗,在微信讀書上線初期作爲主要框架,支撐了後臺服務長達數年。
隨着微信讀書的發展,越來越多異構的系統發展起來,例如推薦算法系統是獨立部署在 TKE 上的容器服務,採用 GO 語言開發,好處是歷史負擔少,運維更加方便、開發更加便捷。兩套系統同時存在帶來的問題是如何做好服務治理,推薦系統需要頻繁調用後臺基礎模塊獲取用戶數據,必須要有一套完善的路由管理、容災機制,且考慮到是異構服務,開發語言也不相同,如果爲每種語言都定製開發一套服務治理框架,代價會非常高。在這個階段,我們開發了 WRMesh 框架,採用 Sidecar+Business 的方式解決這個問題。
Sidecar 專注於處理網絡層的邏輯,和 Business 業務層分開爲兩個進程,由 WRMesh 腳手架生成代碼,上層業務無需感知。Sidecar 集成了 Hikit 框架中用於服務治理的核心邏輯,通過 UnixSocket 與 Business 進行通信,代理 Business 的所有網絡讀寫。當 Business 進程中需要發起網絡請求時,由 WRMesh 生成的 Client 代碼會自動識別當前是否在 mesh 環境中,並轉發請求給 Sidecar,由 Sidecar 完成接下來的網絡處理。因此,Business 進程可以由任意語言任意框架開發,只要遵循 Sidecar 的通信協議,只需要薄薄的一層網絡協議轉換即可接入到 Hikit 的服務治理框架中。另外,對於某些有特殊路由邏輯的 Client,如 KV 訪問、Batch 請求等,代理轉發並不能滿足要求,因此 Sidecar 還提供了插件能力集成這些 Client 邏輯,最大限度爲異構 Business 業務提供原生 C++ 的能力。
隨着 WXG 容器平臺 P6N 的建設越來越完善,許多微信的能力也是基於 P6N 提供,我們也在思考如何逐步遷移到 P6N。由於微信讀書後臺運維目前依賴於企微團隊,有獨立於 P6N 的一套運維體系,我們負責業務和架構開發。如果要一刀切把所有後臺服務遷移至 P6N,將會面臨幾個問題:
-
框架代碼需要重新適配,開發環境和現網環境都有巨大的改造成本
-
遷移不是一蹴而就,後臺上百個服務在遷移過程中,會存在新舊服務互調的問題,由於運維環境不互通,微服務之間無法完成服務治理,這種互相調用最終只能通過 Proxy 來轉發,不僅增加了網絡的失敗率,時延增加,最關鍵的是這個過程會讓容災體系大打折扣
-
存儲模塊的遷移成本和風險巨大,如果不遷移存儲模塊只遷移了邏輯模塊,那勢必又會存在 2 中的問題,這個過程很難收尾
考慮到人力成本及投入性價比,我們最終採用了折衷的方案。一方面我們保留了依賴於企微的運維環境,保障絕大多數現成服務的穩定運行。另一方面,對於微信 P6N 中的服務,我們搭建了比較完善的 Proxy 層,例如 Svrkit 代理、WQueue 代理等,兩套架構可以方便進行互通,最大限度的在原有基礎上接入微信的新能力。目前,微信讀書已順利接入如 WQueue、FKVOL、SimOL、TFCC 等衆多微信的能力。
書籍數據中臺建設
書籍是微信讀書的內容根基,書籍數量的多少、書籍質量的好壞,很大程度上決定了用戶是否選擇微信讀書作爲閱讀 App。過去,我們依託閱文集團提供電子書資源,免去了書籍上架前繁瑣的處理流程,包括排版、審校、元信息管理、更新管理等,後臺服務只需要對接閱文 API 即可方便獲取書籍數據,我們只需要關注書籍在平臺的存儲管理和分發流轉即可。
近幾年,電子書行業的大環境發生變化,一方面,用戶對書籍品類多樣性、內容質量有更高的訴求,另一方面,平臺對成本、版權等行業因素也更爲敏感。因此,我們也在積極探索自籤版權,甚至是自出品的模式,嘗試走更多不一樣的道路。從後臺角度而言,從過去單一依賴閱文集團 API 的模式,慢慢轉爲開放更多的書籍管理接口,形成書籍數據中臺模式,爲上層運營同學搭建內容管理平臺,讓更多人可以方便參與到電子書的製作、排版、上下架、運營管理當中。
以 EPUB 爲例,從內容產出到上架到微信讀書,大致經歷以下階段:
-
排版審校。這個階段多爲人工或者部分機器自動化介入
-
上架預處理。這個階段需要創建書籍信息,配置各種運營策略,當這本書是重排版上架時,內容發生改變,由於現網已經存在用戶的劃線筆記、進度等數據,需要有完善指標評估是否適合覆蓋上架,當上架時,需要對用戶數據進行修復,避免發生錯位情況,嚴重影響用戶體驗
-
EPUB 解析。當書籍上架後,由於 EPUB 是單一文件,不適合解析和管理分發,因此後臺會把源文件解析成自有格式,包括 EPUB 拆章、圖文分離、樣式分離、按章生成離線包等等
-
生成 BookInfo 和 BookData 並落盤。EPUB 文件經過解析後,BookInfo 和 BookData 會存儲到自建的 StoreSvr 服務上,StoreSvr 針對書籍存儲、下載等場景進行了很多優化,具備高可用、低時延的特點,提供了書籍信息獲取、按章下載等核心接口。
回到最初的目標,我們希望把更多的書籍管理能力開放出來,對上層屏蔽電子書底層的後臺邏輯,讓運營同學可以更專注於書籍的管理。因此,我們構建瞭如下書籍數據中臺:
後臺服務拆分開 StoreAPI 和 StoreSvr,StoreAPI 提供書籍管理的接口,由運營同學搭建的內容平臺與 StoreAPI 交互,完成書籍的管理工作。StoreSvr 一方面接受 StoreAPI 的請求,更新書籍數據,另一方面爲現網用戶提供高可用的服務。StoreAPI 提供瞭如下接口能力:
-
書籍 id 分配、上下架
-
書籍信息創建、修改
-
書籍內容修改、連載更新、訂閱推送
-
運營策略管理
此外,如上所述,劃線位置和閱讀進度等核心 UGC 數據由於是按文件偏移記錄,當書籍文件替換後,這些數據會發生錯位,如果不能及時修復,將對用戶體驗造成巨大影響。尤其在一些熱門書籍裏,單本書裏與位置相關的 UGC 數據往往能達到億級別,由於文件替換後位置的偏移具有隨機性,並不能採用簡單的映射方式解決,在過去,我們開發了專門的修復服務來完成這個事情,針對每一個 UGC 內容,採用全文模糊查找的方式重新計算新的偏移,並更新的 UGC 正排、書籍倒排等多個存儲中。但隨着用戶數據越來越多,書籍替換頻率越來越頻繁,修復不及時或者失敗的問題逐漸暴露出來:
-
修復量大導致修復不及時。過去的修復服務雖然是多機部署,但處理單本書仍只是集中在一臺機器上,單機性能有限。
-
修復任務缺乏落盤管理,修復服務一旦重啓,任務丟失。
針對上面的問題,我們重新設計了修復服務,目標是最大限度縮短修復時間,並且讓整個過程是可靠的。爲此,我們先首手考慮業務流程,我們發現在書籍上架前,運營同學本來就需要依賴 UGC 的修復情況做前置判斷是否覆蓋上架,這個過程中雖然是對 UGC 抽樣評估,如果能對這個修復映射結果進行緩存,在正式替換文件後,也能一定程度提升修復速度。在覈心修復流程中,我們進行了較大的重構,把單本書的修復任務拆解成多個子任務,存儲在 Chubby 上,多機器搶鎖共同消費這些任務,由於任務有落盤,在服務上線重啓過程中,也能馬上恢復。修復過程涉及大量的 KV 寫入,併發太高時容易命中單 key 的限頻或者版本衝突,我們爲此開發了針對 K-Str 和 K-Table 的寫入中間件,可以在內存中聚合一批請求進行批量合併寫入,緩解 KV 層面的失敗。
目前,微信讀書已通過內容平臺完成了多家版權方自籤,並在探索自出品等內容創作新形式。
賬號系統可用性建設
賬號是微信讀書後臺系統的基石,承擔了登錄、會話密鑰生成與派發、用戶資料管理等核心功能,所有的用戶請求都需經過賬號系統進行鑑權驗證用戶身份,但凡有一點系統抖動都會影響到整個 App 的正常使用,嚴重者還會導致賬號被踢出無法再次登錄。
賬號系統的架構在微信讀書誕生之初便一直沿用,同一個號段的賬號服務 AccountSvr 和 MySQL 部署在同一臺機器上,備機採用主從同步的方式獲取數據,當主機不可用時,備機承擔了所有讀請求。在某些場景下,爲了能使訪問備機時也具備一定的寫入能力,曾經魔改過主備邏輯,但一切都顯得治標不治本,且引入了更復雜的系統特性,整個架構略顯混亂。在機器裁撤、數據擴容過程中,曾造成過幾次嚴重故障,導致 App 不可用,嚴重影響用戶體驗。究其原因,是因爲當時基礎設施還不完善,缺少高性能高可靠的強一致存儲,MySQL 也是手動搭建的,運維成本和風險都非常高。
爲了徹底解決這個歷史包袱,我們在 2024 下定決心對其進行重構。重構就意味着要拋棄現有 MySQL 這套臃腫的存儲方案,把數據遷移到新的存儲組件上,這裏涉及到的挑戰點如下:
-
賬號鑑權服務訪問量巨大,遷移過程須儘量不增加系統負擔,且必須是在不停機的情況下進行
-
遷移過程中一旦有數據丟失或者錯誤,會導致用戶資料受損,用戶登錄態丟失,App 無法使用
-
賬號系統還涉及用戶 id 分配和回收邏輯,在切換存儲時如何保證數據的一致性,不重複分配號碼
背水一戰,沒有退路可言。在經歷了多次論證後,我們決定採用 Paxosmemkv 作爲新的存儲組件,全內存、多副本、強一致的特性,很適合作爲賬號系統的底層存儲。同時,我們爲整個遷移過程制定了周密的方案,把每一步進行了分解,且要求每個環節可灰度可回退,同時要做好數據的一致性檢查。在完成數據遷移後,我們還需要對 AccountSvr 進行重構,拋棄按號段的賬號分配、路由、緩存邏輯,以全新的視角設計更簡潔的架構。
內容召回演進
以往微信讀書的搜索僅限於基於書名、作者等維度的文本召回,通過自建的全內存索引服務實現書籍的檢索。全文檢索則基於 ES 搭建,採用規則分段的方式建立索引,能滿足讀書大部分場景的需要。
在大語言模型迅速發展的近兩年,微信讀書作爲一個龐大的內容知識庫,具有大量的書籍原文資源,同時,用戶在微信讀書也留下了大量的文字內容,如書評、想法等,這些內容構成了 AI 問書的內容基石,也是 AI 問書區別於其它問答工具的核心優勢。基於微信讀書構建 RAG 召回系統,核心挑戰如下:
-
基於書籍原文構建全文檢索,爲了達到最好的效果,往往需要支持按語義進行段落切分,在此基礎上構建 embedding 進行語義召回。微信讀書擁有百萬級書籍原文數據,此外,對於用戶導入書,更是達到億級別規模。現有架構無論從成本還是耗時上都無法解決。
-
爲了支持更多維度的召回,需要對 UGC 內容進行召回,部分 UGC 內容屬於私密信息,並不向全網公開,只需要滿足用戶個人檢索即可。此時如果用常規的檢索系統構建常駐索引,訪問率太低,成本難以收斂。
爲此,我們針對微信讀書不同的 RAG 使用場景,設計瞭如下召回架構:
我們把數據劃分成兩類:全局公開可搜以及用戶個人可搜。
對於全局公開可搜索的數據,如庫內電子書的全文、書籍大綱、書評、人工知識庫等,我們構建了一套入庫流程,能對源信息進行語義分段、生成正排倒排,語義分段基於開源的 chunk 模型進行微調,正排基於 fkv,倒排則基於 ES 構建,ES 提供了 DiskANN 方案,通過設置合理的緩存和分片,能在存儲成本和召回效率之間取得不錯的平衡。對於 App 內主搜等低時延場景,爲了滿足多種定製化檢索需求,我們自建了基於內存索引的 Searchsvr 服務,支持索引落盤,可以在毫秒級返回電子書搜索結果。
對於用戶個人數據,如導入書全文、個人想法等,特點是數據量大但使用頻率不高,不需要針對全網用戶進行檢索,如果採用上述方案,會帶來成本災難,性價比極低。爲此,我們按用戶及物料的維度,基於 USearch、Xapian 等方案構建了向量及文本索引,這些組件的優勢在於可以把單個索引存儲成文件的形式,便於落盤,配合一些量化的方法,可以把大大壓縮索引大小。在構建索引階段,按用戶 + 類型構建出不同的索引,並存儲在低成本的 COS 上,當用戶需要檢索召回時,採用讀時加載的方式實時進行召回,結合 CFS 進行預熱可以大大提升檢索速度。當檢索完成後,定時淘汰策略會把長期不用的索引從 CFS 中清理,降低存儲成本。
寫在最後
雖然微信讀書已經發展了十個年頭,但我們的腳步從未停止。
在日常業務開發之餘,我們也從未停止思考如何讓系統能走得更遠、更穩健,抓住每一個可能的優化點,隨時做好準備,迎接下一個精彩的十年。
原創作者|羅國佳
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/jpTezo6097QymO0GODidqw