西瓜視頻基于 Hertz 的微服務(wù)落地實(shí)踐
01、西瓜視頻微服務(wù)架構(gòu)設(shè)計(jì)
西瓜視頻介紹
西瓜視頻是一個(gè)開眼界、漲知識(shí)的視頻 App (Informative Video Platform),作為國(guó)內(nèi)領(lǐng)先的中長(zhǎng)視頻平臺(tái),它源源不斷地為不同人群提供優(yōu)質(zhì)內(nèi)容,讓人們看到更豐富和有深度的世界,收獲輕松的獲得感,點(diǎn)亮對(duì)生活的好奇心。
同時(shí),西瓜視頻鼓勵(lì)多樣化創(chuàng)作,幫助人們輕松地向全世界分享視頻作品,創(chuàng)造更大的價(jià)值。目前平臺(tái)月活躍創(chuàng)作人超過 320 萬,月活躍用戶數(shù)超過 1.8 億,日均播放量超過 40 億,用戶平均使用時(shí)長(zhǎng)超過 100 分鐘。
微服務(wù)架構(gòu)設(shè)計(jì)時(shí)關(guān)注哪些方面
業(yè)務(wù)域劃分
上面四張圖分別對(duì)應(yīng)我們的中視頻、電商、長(zhǎng)視頻和作者側(cè)的業(yè)務(wù)。當(dāng)然我們的業(yè)務(wù)場(chǎng)景遠(yuǎn)不止這些,但是也反映出了 C 端場(chǎng)景多,業(yè)務(wù)域劃分較細(xì)的特點(diǎn),因此我們?cè)谖⒎?wù)架構(gòu)設(shè)計(jì)的時(shí)候需要著重考慮服務(wù)劃分與解耦,在設(shè)計(jì)期間我們主要遵循兩個(gè)原則:
- 單一職責(zé)原則:確保每個(gè)微服務(wù)只負(fù)責(zé)一個(gè)特定的業(yè)務(wù)功能,避免職責(zé)混亂。
- 領(lǐng)域驅(qū)動(dòng)設(shè)計(jì):使用領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)方法來劃分服務(wù)邊界,確保各個(gè)服務(wù)獨(dú)立、可復(fù)用。
這樣設(shè)計(jì)之后,可以保證:
- 業(yè)務(wù)模塊的獨(dú)立性:每個(gè)微服務(wù)可以獨(dú)立開發(fā)、測(cè)試、部署和擴(kuò)展,提升了開發(fā)效率和系統(tǒng)的靈活性。
- 技術(shù)棧靈活性:不同的微服務(wù)可以使用最適合其業(yè)務(wù)需求的技術(shù)棧,不需要統(tǒng)一技術(shù)選型。
- 故障隔離:一個(gè)服務(wù)的故障不會(huì)影響其他服務(wù)的運(yùn)行,提高了系統(tǒng)的可用性和穩(wěn)定性。
- 按需擴(kuò)展:可以根據(jù)每個(gè)服務(wù)的負(fù)載情況獨(dú)立擴(kuò)展,優(yōu)化資源使用和成本。
性能
架構(gòu)設(shè)計(jì)
上圖是西瓜視頻整體的微服務(wù)架構(gòu)設(shè)計(jì)。
從上到下我們分為三層,分別是接入層,業(yè)務(wù)層和基礎(chǔ)組件層。
接入層
- 不同的分端:包括西瓜 APP,西瓜 PC,TV 端鮮時(shí)光以及 M 站。
- 負(fù)載均衡及網(wǎng)關(guān)
使用公司內(nèi)部組件來提供負(fù)載均衡和通用網(wǎng)關(guān)能力。
- API 服務(wù):使用 Hertz 框架的服務(wù)。
業(yè)務(wù)層
- 消費(fèi)側(cè)業(yè)務(wù),基礎(chǔ)業(yè)務(wù),和其他的一些互動(dòng)社區(qū)等 RPC 業(yè)務(wù),這些服務(wù)采用的是 Kitex 框架。
消費(fèi)側(cè)業(yè)務(wù)主要面向 C 端用戶的場(chǎng)景就是信息流和詳情頁。
信息流:主要是西瓜 APP 的全部頻道模塊,包括進(jìn)入西瓜視頻后的首頁看到的推薦精選都對(duì)應(yīng)我們的信息流服務(wù)。
詳情頁:我們知道推薦頻道更多對(duì)應(yīng)的是一個(gè)沉浸式的場(chǎng)景,但是我們的視頻也會(huì)有其他的入口進(jìn)入,比如個(gè)人主頁等場(chǎng)景,這個(gè)時(shí)候點(diǎn)擊一個(gè)視頻就會(huì)進(jìn)入詳情頁,相對(duì)于沉浸式那種更專注于視頻本身播放的體驗(yàn),詳情頁會(huì)包含更多的視頻信息。
推薦系統(tǒng):這里是一些推薦、廣告的服務(wù),主要是提供底層的數(shù)據(jù)的 id 返回,包含廣告混排,推薦排序,廣告投放等功能。
打包服務(wù):公司的業(yè)務(wù)線會(huì)比較繁雜,所以隨著發(fā)展抽象出了打包層,各個(gè)業(yè)務(wù)會(huì)有自己數(shù)據(jù)結(jié)構(gòu)的打包服務(wù),作為最終返回給客戶端的數(shù)據(jù)。這里就包括小視頻、中視頻、長(zhǎng)視頻、直播等數(shù)據(jù)的打包。
- 互動(dòng)模塊
在這些業(yè)務(wù)之外,我們還會(huì)有其他的一些互動(dòng)和社區(qū)的功能模塊。這種都是有公司專門的關(guān)系中臺(tái)、評(píng)論中臺(tái)去維護(hù)。同時(shí),我們的業(yè)務(wù)還會(huì)依賴業(yè)務(wù)用到的存儲(chǔ),像 mysql、redis 等。
基礎(chǔ)組件
基礎(chǔ)組件,比如語言框架,像前面提到的 Hertz、Kitex 等框架,還有一些日志、監(jiān)控,配置系統(tǒng)。
02、Hertz 框架介紹
背景
字節(jié)跳動(dòng)從 2014 年開始使用 Golang,2016 年基于 Gin 框架封裝了 Ginex。但 Ginex 在迭代受開源 Gin 項(xiàng)目限制、代碼混亂膨脹導(dǎo)致維護(hù)困難、無法滿足性能敏感和功能擴(kuò)展需求等問題。2020 年部分業(yè)務(wù)線嘗試魔改其他開源框架如 Fasthttp,但帶來了分散生產(chǎn)力和巨大維護(hù)成本的問題。為解決這些痛點(diǎn),字節(jié)于 2020 年初立項(xiàng)開發(fā)自研高性能 Go 框架 Hertz,經(jīng)過兩年多的迭代,Hertz 于 2022 年 6 月正式開源,目前已廣泛應(yīng)用于字節(jié)內(nèi)部逾 1.4 萬個(gè)服務(wù),支撐日峰值 QPS 超 5000 萬,顯著降低資源使用和服務(wù)延時(shí),接替大量基于 Gin 的存量服務(wù),助力公司降本增效。
為什么選擇 Hertz
- 極致的性能
Hertz 的性能指標(biāo)是作為一個(gè)核心指標(biāo)開展設(shè)計(jì)和實(shí)現(xiàn)的。Hertz 默認(rèn)使用自研的高性能網(wǎng)絡(luò)庫 Netpoll,在一些特殊場(chǎng)景相較于 go net,Hertz 在 QPS、時(shí)延上均具有一定優(yōu)勢(shì)。關(guān)于性能數(shù)據(jù),大家可以看一下 hertz-benchmark(https://github.com/cloudwego/hertz-benchmark),可以看到 Hertz 的 QPS 和時(shí)延指標(biāo)在和其他三個(gè)知名框架對(duì)比中已經(jīng)全面占優(yōu),對(duì)比 Gin 更是遙遙領(lǐng)先。
并且,框架整體的持續(xù)優(yōu)化會(huì)貫穿框架的整個(gè)生命周期,持續(xù)不斷地進(jìn)行下去。
- 易用性,開發(fā)者友好
在開發(fā)過程中,快速寫出正確的代碼往往是重要的。Hertz 在設(shè)計(jì) API 時(shí),考慮到用戶的使用習(xí)慣,參考業(yè)界主流框架使用 API 的方式,并加以優(yōu)化。在 Hertz 在迭代過程中,積極聽取用戶意見,持續(xù)打磨框架, 比如很多用戶希望 Client 也有 Trace 的能力,為此,Hertz Client 支持了中間件能力。Hertz 也提供了命令行工具,一鍵生成代碼,提高框架的易用性。
Hertz 提供了一個(gè)簡(jiǎn)單易用的命令行工具 hz,用戶只需提供一個(gè) IDL,根據(jù)定義好的接口信息,hz 便可以一鍵生成項(xiàng)目腳手架,開箱即用使用 Hertz;hz 也提供更新能力,用戶的 IDL 如果發(fā)生改變,hz 可以更新腳手架。目前 hz 支持了 Thrift 和 Protobuf 兩種 IDL 定義。命令行工具內(nèi)置豐富的選項(xiàng),可以根據(jù)自己的需求使用。
- 豐富的文檔體系
即使是從來沒有使用過相關(guān)框架的新同學(xué),都能夠通過相應(yīng)的文檔,快速上手 Hertz,體驗(yàn) Hertz 所帶來的極致的開發(fā)體驗(yàn)。
- 穩(wěn)定性
Hertz 對(duì)內(nèi)對(duì)外支撐了大量的業(yè)務(wù)服務(wù),業(yè)務(wù)選擇 Hertz 的一個(gè)重要考量就是穩(wěn)定性。Hertz 也制定了各種措施來保證框架的穩(wěn)定性: - 基于線上流量特征的隨機(jī)模擬。Hertz 會(huì)根據(jù)線上真實(shí)的流量曲線,通過模擬和重放的方式,在測(cè)試環(huán)境中復(fù)現(xiàn)各種極端的高并發(fā)場(chǎng)景,從而驗(yàn)證系統(tǒng)的承載能力和容錯(cuò)能力。
- 核心 API 全覆蓋。Hertz 所有對(duì)外提供服務(wù)的 API 接口,無一例外都要經(jīng)過完備的性能測(cè)試、壓力測(cè)試和負(fù)載測(cè)試,確保在各種極限情況下也能保持穩(wěn)定和高效。
- 適配不同的線上部署環(huán)境。針對(duì) Hertz 服務(wù)的不同場(chǎng)景,會(huì)準(zhǔn)備不同的線上環(huán)境。這些環(huán)境硬件資源配置不盡相同,通過在不同環(huán)境中反復(fù)測(cè)試和驗(yàn)證,可以全面評(píng)估系統(tǒng)的穩(wěn)定性和可擴(kuò)展性。
- 7*24 小時(shí)監(jiān)控大盤。全天候?qū)崟r(shí)監(jiān)控系統(tǒng)的各項(xiàng)核心指標(biāo),一旦發(fā)現(xiàn)異常,能夠第一時(shí)間定位并通知值班人員介入處理。
- 嚴(yán)格的發(fā)布流程。Hertz 對(duì)系統(tǒng)的每一次上線升級(jí)都有特別嚴(yán)格的流程要求,必須經(jīng)過線下測(cè)試、驗(yàn)證、代碼 review 等多個(gè)環(huán)節(jié),避免出現(xiàn)變更帶來的風(fēng)險(xiǎn)。
- 低廉的遷移成本
- 西瓜早期的 API 服務(wù)都是基于 Ginex 框架開發(fā)的。而 Hertz 立項(xiàng)之初,就將針對(duì)存量 Ginex 服務(wù)的遷移作為一個(gè)高優(yōu)需要照顧的環(huán)節(jié)。在一些業(yè)務(wù)常用 API 上做了許多兼容性的設(shè)計(jì)。
- 同時(shí),Hertz 也為存量項(xiàng)目提供了一鍵遷移的方案 Ginex 項(xiàng)目快速遷移 Hertz 1.x 指南,使用一鍵遷移工具,用戶可能無需/只需改動(dòng)少量代碼即可完成
- Gin —> Hertz 項(xiàng)目的遷移。詳見:https://www.cloudwego.io/zh/docs/hertz/tutorials/service-migration/#gin
- 豐富的擴(kuò)展能力
Hertz 采用了分層設(shè)計(jì),提供了較多的接口以及默認(rèn)的擴(kuò)展實(shí)現(xiàn),用戶也可以自行擴(kuò)展。Hertz 目前支持了日志、監(jiān)控、服務(wù)注冊(cè)與發(fā)現(xiàn)、網(wǎng)絡(luò)庫和協(xié)議等擴(kuò)展能力,未來有望為用戶提供更多的擴(kuò)展能力。
03、Hertz 遷移過程、踩坑經(jīng)驗(yàn)
遷移過程
1. 選擇要遷移的服務(wù)
遷移的第一步就是要先選擇要遷移的服務(wù),根據(jù)帕累托原則,我們優(yōu)先找出占據(jù)最大比例的成本來源,并處理這些高成本項(xiàng)目,以最大化資源利用效率。根據(jù)性能分析平臺(tái),我們選擇了目前西瓜 CPU 資源消耗最大的兩個(gè) api 服務(wù)進(jìn)行遷移,分別對(duì)應(yīng)我們的觀看歷史、點(diǎn)贊和彈幕的有關(guān)服務(wù)。
2. SDK 庫適配
西瓜業(yè)務(wù)線對(duì) Ginex 做了封裝,提供一個(gè) SDK 給其他 api 服務(wù)使用,比如在獲取觀看歷史的接口中,我們會(huì)先使用 SDK 中的 newRequestContext 方法來做一些請(qǐng)求上下文的初始化操作。在這個(gè)請(qǐng)求上下文中我們會(huì)有 ab 實(shí)驗(yàn)、設(shè)備信息、地理位置信息等上下文的初始化。所以我們第一步就是針對(duì) SDK 庫中相應(yīng)的代碼進(jìn)行適配。在這個(gè)適配過程中有一些三方的 SDK 庫的依賴,可以直接升級(jí) SDK 對(duì)應(yīng)的 Hertz 版本。
3. 執(zhí)行一鍵遷移腳本
Hertz 同學(xué)提供了一鍵遷移腳本,只需在當(dāng)前服務(wù)目錄下執(zhí)行腳本即可完成大部分重復(fù)性的改造工作。
腳本通過正則匹配的方式完成大部分重復(fù)性替換工作,對(duì)于一些 gin、ginex 字樣會(huì)替換為Hertz 中的相應(yīng)的實(shí)現(xiàn)。
4. 手動(dòng)修改
Hertz 中間件的設(shè)計(jì)采用了洋蔥模型,洋蔥模型是一種中間件流程控制方式,做到核心邏輯和通用邏輯分離。
中間件可以做日志記錄、性能統(tǒng)計(jì)、安全控制、事務(wù)處理、異常處理等場(chǎng)景。
Hertz 預(yù)置了幾款中間件:
- Recovery 中間件:負(fù)責(zé)處理鏈路上的 panic
- Metrics 中間件:負(fù)責(zé)請(qǐng)求相關(guān)指標(biāo)上報(bào)
對(duì)于業(yè)務(wù)自己實(shí)現(xiàn)的特殊中間件需要進(jìn)行遷移,關(guān)于如何實(shí)現(xiàn)一個(gè)中間件 Hertz 也給出了實(shí)例。
以西瓜業(yè)務(wù)自己的特殊中間件 default_stable 為例,我們會(huì)在 header 中將 stable 設(shè)置為 1,用于做 SLO 數(shù)據(jù)統(tǒng)計(jì)。這種中間件我們就需要做一些適配工作,這里的話就會(huì)面臨接口不匹配的問題,可以參考 Gin —> Hertz API 對(duì)照表(https://github.com/hertz-contrib/migrate/blob/main/gin_to_hertz.md)進(jìn)行修改。
踩坑經(jīng)驗(yàn)
1. Query tag 缺失導(dǎo)致請(qǐng)求參數(shù)解析失敗
- 表現(xiàn)
Query 請(qǐng)求參數(shù)在服務(wù)端沒有解析成功,調(diào)用 RequestContext 進(jìn)行參數(shù)綁定的時(shí)候報(bào)錯(cuò)。
- 原因
- 參考 Hertz 支持的參數(shù)綁定與校驗(yàn)相關(guān)功能及用法,不通過 IDL 生成代碼時(shí)若字段不添加任何 tag 則會(huì)遍歷所有 tag 并按照優(yōu)先級(jí)綁定參數(shù),添加 tag 則會(huì)根據(jù)對(duì)應(yīng)的 tag 按照優(yōu)先級(jí)去綁定參數(shù)。
Hertz 支持的參數(shù)綁定與校驗(yàn)相關(guān)功能及用法:
https://www.cloudwego.io/zh/docs/hertz/tutorials/basic-feature/binding-and-validate/ - 請(qǐng)求參數(shù)有關(guān)的結(jié)構(gòu)體未設(shè)置 query tag,導(dǎo)致 query 請(qǐng)求參數(shù)在服務(wù)端沒有解析成功。
- 解決方式
- 結(jié)構(gòu)體添加 query tag
- 通過單元測(cè)試提前規(guī)避
2. 未配置 looseZero 模式導(dǎo)致綁定數(shù)值類型報(bào)錯(cuò)
- 表現(xiàn)
在一些場(chǎng)景下,前端有時(shí)候傳來的信息只有 key 沒有 value,這會(huì)導(dǎo)致綁定數(shù)值類型的時(shí)候報(bào)錯(cuò)。
- 原因
- 未配置 looseZero 模式
- 解決方式
04、落地 Hertz 后的收益
上線前后對(duì)比發(fā)現(xiàn) CPU 使用率下降約 10%,優(yōu)化效果明顯,詳細(xì)的性能收益如下:
- 平均 CPU 核數(shù)從 2730 降為 2443
- 單核 QPS 處理能力提升:10.7%
- 內(nèi)存占用率變化—優(yōu)化前:31.1% ,優(yōu)化后:29.3%
- 核心接口 LatencyPct99 下降:10.15%
項(xiàng)目地址
GitHub:https://github.com/cloudwego
官網(wǎng):www.cloudwego.io