短視頻 go 研發框架實踐
導讀:hulk 是短視頻研發部研發的基於 GDP2(Go Develop Platform )的 go 服務開發框架。它是⼀款⾯向業務的 Web 開發框架,提供了諸多開箱即⽤的組件和功能,可以⽤來快速開發 Web 服務。同時,依託於 hulk 框架並結合⼚內 / 業界優秀的開發實踐,初步構建了⼀個符合業務應⽤場景的 go ⽣態體系。
一、產生背景
hulk 框架是在 “好看視頻” 服務端的 go 服務化架構升級背景下產生的。
1.1 爲什麼要做架構升級?當前架構面臨哪些問題?
好看視頻初期因業務需要快速、靈活的開發迭代,採⽤ PHP 作爲開發語⾔實現後端服務,前期取得了⽐較好的開發迭代效果。但隨着好看業務快速發展,服務端的項⽬ (接⼝、代碼等) 急速膨脹,類單體的 PHP 架構在多個⽅⾯遇到了瓶頸和問題,主要體現在以下⼏個⽅⾯:
1. 開發效率:對於主代碼庫,所有服務端同學都會在這同一個代碼空間開發,此外還有依賴的第三方團隊也會修改,頻繁的修改 / 合併降低了開發效率,同時也加大了代碼的維護成本和難度;
2. 上線效率:多用戶開發同一代碼庫的另一個弊端就是上線等待,由於同一個時刻只能有一個分支上線(分級上線),導致相連的上線需求要排隊等待。這也導致我們的同學摸索出 “搭車上線” 的模式,雖然加快了上線效率,但也加大了上線的風險,沒有從根本上解決問題;
3. 運行效率:PHP 在開發效率和靈活度方面確實有一定優勢,但當所支撐的業務達到幾千萬 DAU 及以上時,我們必須要考慮服務的運行效率和資源成本等問題。PHP 語言在多線程 / 多協程的支持上,弱於 Java、C/C++、Go 等語言,基於物理機部署的類單體服務部署架構,在資源利用率和服務擴縮容等方面也很難滿足需求;
4.SRE 效率:在出現穩定性問題時,我們期望能夠做到快速感知、快速定位、快速止損。目前基於 sia 的監控 / 報警,基於日誌的問題定位方式距離理想目標還有一定的距離:一是同學要奔波於各個平臺 / 系統獲取問題線索,二是獲取到的線索及信息維度很多時候也無法滿足快速、精確定位問題的需求;
這些問題需要通過 “4 化”,從總體業務架構、部署架構、基礎設施等多方面去解決:
1.2 爲什麼不直接基於 GDP2 ?
好看的 go 服務化升級工作開展時,GDP2 還未正式發佈,這也是其中一個因素。
1.3 hulk 與 gdp2 能⼒對照
下⾯從三個⽅⾯與 gdp2 做⼀個簡單的對照,初步瞭解 hulk 的整體能⼒及與 gdp2 的⼀些差異。
1.3.1 web server 能⼒
hulk ⽬前主要服務於 web 應⽤,⾸先了解⼀下 hulk 的 web server 能⼒。
1.3.2 功能 / 組件
功能 / 組件的豐富度及⾃身能⼒,很⼤程度上影響了框架對業務服務的⽀持能⼒。備註:ral 資源訪問層
1.3.3 框架周邊及基礎設施
框架從來不是 “單打獨⽃的”,它需要有周邊⼯具和基礎設施來⽀持。
NOTE:
- 好看在做 go 化時,也調研了開源社區⾥⽐較優秀的⼀些⼯具系統和⽅案並引⼊, hulk 中默認添加了對這些基礎設施的集成;
1.3.4 對照總結
本節主要站在 hulk 能力角度與 GDP2 做了一些方面的參考對照。以上對照,可以概括爲 4 點:
-
很多基礎能⼒,hulk 是復⽤ gdp 的,如:bns、net、codec 等;
-
⼀些通⽤ / 擴展組件,hulk 按照業務需求場景,進⾏⼆次封裝和增強,如:httpserver、ral、redis、mysql 等;
-
對於 gdp ⽬前沒有⽀持的⼀些業務需求,進⾏開發集成,如:定時任務、配置中⼼、服務治理等;
-
參考業界開源實踐,引入了一些新的基礎設施:如 prometheus+grafana 集羣、sentry 集羣、故障定位系統等;
GDP2 由幾十個模塊共同構成,由於時間有限,可能個別功能點的對照有偏差。
二、瞭解 hulk
2.1 設計思路
2.2 框架結構
從功能上來看,hulk 的整體能力可以劃分爲四層:
2.2.1 基礎組件
提供了絕大部分項目都應該需要的基礎能力,也是其他上層功能組件很可能依賴的組件。hulk 框架通過這些基礎組件,使上層應用可以無感的與基礎設施進行集成:
-
日誌組件:默認支持與 PHP 兼容的打印格式(用於配置 sia 監控和報警),同時也兼容 ftrace 接入的格式(日誌查詢和問題定位);
-
雲原生監控:默認支持 prometheus,對所有接口請求、redis、ral 等遠程調用進行多維度的 metrics 採集,並通過 grafana 展示;
-
配置中心:通過配置中心,可以實時下發並生效配置。目前支持 Apollo/iConf,支持功能包括 - 版本管理、熱發佈、灰度發佈、權限管理、審覈與審計等;
-
事件追蹤 / 定位:藉助 sentry,對於一些故障,我們可以秒級感知。hulk 在異常信息中保存了比較完整的現場信息 - 如調用棧、request、集羣和實例信息等,通過這些信息,可以直接定位問題的原因;
2.2.2 通用組件
這一層的組件能力是通用的,提供了一些管理控制和切面能力:
-
ral 組件:hulk 的 ral 模塊封裝了 GDP2 的 ral 主體功能,同時,對 ral 進行了增強 - a) 提供了通過字符串而非文件來進行 ral 初始化和 ral 懶加載功能;b) 提供了多個 hook 能力,如 prometheus 的監控信息採集,熔斷、降級等;
-
服務治理:框架的服務治理能力是基於 Sentinel (阿里開源的高可用流量防護組件)和配置中心來構建的,主要以流量爲切入點,從限流、流量整形、熔斷、降級等多個維度來協助保障微服務的穩定性,並提供動態控制能力;
-
協程池:a) 可以自動調度海量協程,複用 goroutines,減少 gc,b) 可以優雅處理 panic,防止程序崩潰 c) 提供了:任務提交、獲取運行中的 goroutine 數量、封裝了 WaitGroup 支持協程任務編排等功能;
-
事件通知:框架與如流做了集成。用戶將 robot token 配置在項目裏,就可以直接使用 ruliu 組件向指定的如流羣發送報警 / 通告。如流組件結合 sentry,可以讓我們第一時間知道程序出了問題並快速定位到問題;
2.2.3 擴展組件
前兩層功能對直接的業務處理邏輯參與較少,這一層的組件其能力多是爲了處理某一類特定業務邏輯和場景,如 redis/mysql / 定時任務等:
1.redis 組件:基於 GDP2 redis 模塊的封裝並作了功能增,提供了:
a) metrics hook,對所有的 redis 請求進行監控 (prometheus) 打點(latency/p99/qps / 錯誤碼分佈等);
b**) sentry hook**,支持將 redis 錯誤在記錯誤日誌同時發送到 sentry;
c) 降級 hook,支持按集羣 / 實例 / 百分比維度降級 redis 訪問;
d) 熔斷 hook,支持按集羣 / 實例 / 錯誤率 / 慢請求率對依賴的服務進行熔斷設置;
2.mysql 組件:mysql 組件是基於 GDP2 mysql 和 gorm_adapter 的封裝,在已有能力之上,進行了以下功能擴展:
a) 提供了 metrics hook,對所有的 mysql 請求進行監控 (prometheus) 打點(latency/p99/qps / 錯誤碼分佈等);
b) 提供了 sentry hook,支持將 mysql 錯誤在記錯誤日誌的同時發送到 sentry;
3. 分佈式鎖:hulk 提供了基於 redis 的分佈式鎖實現。其中 redis 連接是基於 GDP2 的 redis 模塊的改造,分佈式鎖功能是封裝了開源項目 redsync;
4. 定時任務:支持兩種定時任務模式;
a) 帶分佈式鎖的運行方式:對於多實例部署的定時任務,如果任務不是冪等的,則需要使用分佈式鎖對任務的調度運行進行控制;
b) 不帶分佈式鎖的運行方式:此模式下,如果部署了多實例,則所有實例上同一時刻的定時任務,會同時執行;
2.2.4 http server
hulk(目前只提供了 http server 能力)提供了很多通用且高效的 http middleware,並對外暴露了一些管理控制接口,在一些特殊情況下,可以通過這些管理接口,在運行時干預服務的運行:
-
logger_middleware:用於記錄 http 的請求、響應、耗時等信息,同時支持實時修改日誌打印策略 - 如按 idc/ip / 百分比 / uid/cuid 等維度打印;
-
timer_middleware:用於 http 請求的監控埋點,可以輸出可用性、tp99、流量、平響、錯誤碼等 metrics,維度包括服務級 / idc/instance 等;
-
recover_middleware:用於捕獲 http 請求鏈路中的 painc 事件,並可自定義 panic handler 邏輯,如通過結合 sentry 和如流,可以實時感知並定位 panic 事件;
-
flow_control_middleware:接口限流組件,可以通過配置中心或管理接口,對接口按 idc/instance 維度進行限流;
-
timeout_middleware:通過該 middleware 或與配置中心結合使用,可以對接口按 idc 維度進行超時控制;
-
其他 middleware 可以查看 hulk 文檔
(如 - internal_user_middleware、jager_opentracing_middleware、thirdparty_auth_middleware、b2logger_middleware 等)
-
管理控制接口:如健康檢查接口,服務治理 - 熔斷、限流、降級接口,metrics 接口,線上實例性能調試接口等;
2.3 框架生態
通過近一年的建設,我們初步構建了一個以 hulk 框架爲中心的、符合好看業務場景的 go 生態體系,包括:
-
標準目錄規範:避免各個項目結構不統一,減少項目維護難度和工作量;
-
代碼生成器:基於 hulk 框架、標準目錄規範、組件使用規範的代碼生成器,目的是減少通用模塊 / 組件使用不規範,解決通用流程編碼、處理不一致的問題;
-
hklib:好看的通用 lib 庫,提供了一些的通用功能(也包含了很多 PHP 轉 go 過程中的一些 orp 通用 / 基礎的函數 / 功能),也提供了 50 + 對中臺服務的調用 client,減少重複代碼,提升研發效率,提升可維護性;
-
基礎設施:prometheus+thanos 集羣、sentry 服務、apollo 集羣、pyroscope 性能分析平臺等;
-
iconf:好看自研配置中心,能力在對齊開源的 Apollo 之外,還增加 / 增強了一些功能,如 - key 維度的發佈、更安全的配置獲取、更簡潔的操作頁面、類分級發佈等;
-
artemis:服務可視化與故障智能定位系統,可以在該系統中看到服務的部署架構、服務內部調用鏈、多維度細粒度的近實時監控和關鍵日誌。在發生可用性故障時,一些故障問題可以秒級的定位到原因和具體代碼;
2.4 框架應用情況
目前短視頻所有 go 服務都是基於 hulk 構建的,在資源、接口性能和可用性等方面都有一些階段性產出和收益。
hulk 框架應用現狀:
資源和性能收益:
資源和性能收益,很大一部分要歸屬於 PHP->Go 的技術棧切換;而框架爲服務應用相應技術棧特性提供了便捷和高效的方式。
2.4 hulk 服務架構
下圖描述了一個微服務(基於 hulk)的架構全景圖:
-
框架中個各功能組件都是圍繞業務各個場景和需求的,在業務邏輯中能夠比較便捷的使用相關功能組件;
-
這些組件在啓用後,也會與相應的基礎設施進行交互融合,共同支撐服務的高效、可控和穩定的運行;
hulk 組件初始化及與周邊基礎設施的集成,基本都可以通過環境變量 / 配置文件來完成。
三、框架能力與應用
下面我們從日常開發遇到的一些痛點,來介紹框架的能力,並配以示例來說明這些能力是如何減少或解決痛點的。
3.1 如何提升代碼質量?
代碼質量會直接或間接的產生以下影響:
-
代碼質量會直接影響代碼維護成本;
-
代碼質量會影響程序出 bug 的概率;
-
代碼質量會影響程序運行效率;
hulk 框架從以下三方面分別來提升代碼質量。
3.1.1 規範代碼組織結構
降低項目維護成本,提升研發效率。
-
通過標準目錄規範,定義通用 (http 服務) 的項目 layout,避免出現每人一種或多種 layout,最終項目結構 “百花齊放” 的現象;
-
通過代碼生成器,幫助開發者生成項目模板,對初始化流程,各目錄 / 文件的使用進行潛在約定;
3.1.2 編碼規範和靜態檢查
提升代碼可讀性,減少低級代碼 bug
-
遵循百度 Go 編碼規範 + 業務編碼補充規範;
-
使用 GDP 的代碼檢查工具:go_fmt、goc;
3.1.3 配套的壓測和性能分析平臺
確定服務的壓力邊界,發現潛在的性能問題。
- 壓測和性能測試平臺(測試環境):nGrinder
- 程序性能分析平臺:pyroscope。可以通過 hulk 自集成的管理接口,實時打開或關閉線上實例的 “continuous-prof” 功能,定位線上性能問題:
3.2 如何提升開發迭代速度?
-
如何讓開發者專注於業務邏輯與實現?
-
如何讓開發者快速響應並完成產品需求?
hulk 框架爲提升迭代速度,提供了以下支持。
3.2.1 豐富的實用組件 / 功能
提升研發效率,避免試錯,減少出錯。
- 程序增強組件:增強的 redis/mysql 功能,增強的 ral 調用等。例 - 下圖中的 redis 監控,其監控指標是由 hulk redis 組件自動採集計算的:
- 優秀的開源組件:sentry、prometheus+grafana、apollo、協程池等。例 - prometheus+grafana:hulk 框架默認支持 prometheus,可以對服務的可用性、QPS、耗時、錯誤碼等 metrics 自動計算收集:
- 豐富的 http middleware。
3.2.2 配置化、低代碼支持
減少代碼的修改和上線,提升需求的響應和完成速度。
-
hulk 框架中大部分組件可以通過環境變量 / 配置文件來初始化;
-
業務邏輯中的可變數據與配置,可以通過 apollo/iconf 實時下發和生效,無需代碼修改和長流程上線。例 - 可以通過開箱即用的配置中心功能,實時下發並生效配置:
3.3 如何快速感知並定位問題?
-
開發者如何快速感知服務中的問題,嚴重問題如何實時感知?
-
開發者如何能從監控、日誌、報警中獲得詳細的問題信息,以快速定位問題?
hulk 爲提升 SRE 效率,從以下幾個方面提供支持。
3.3.1 完善的事件追蹤定位與通告能力
能夠實時追蹤開發者自定義的錯誤並通告
- 實時事件追蹤組件:sentry。hulk 提供了開箱即用的 sentry 組件功能,可以像打印日誌一樣使用,sentry 中的信息包含代碼調用棧、上下文、自定義關鍵信息等:
- 通告組件:ruliu。一行 token 配置就可以開啓如流功能,可以將一些需要立即關注的信息實時打到如流羣裏,同時還可以和 sentry 結合,實現異常問題實時感知和定位:
3.3.2 prometheus+sia 監控支持
通過 prometheus 與 noah 的互補,支持多維度全方位監控,能夠獲得更多的服務穩定性相關信息
-
prometheus 爲開發者提供靈活的多維度的業務監控信息;
-
sia 可以爲開發者提供基於日誌的採集的服務穩定指標和容器、網絡等資源維度監控信息;
3.3.3 ftrace 日誌查詢與分析功能
hulk 默認支持 ftrace 平臺的日誌格式
- 通過 ftrace,可以便捷高效的查詢用戶維度的日誌信息;
- 通過 pdo2 命令,可以檢索查詢自定義規則的日誌信息;
3.4 基於 hulk 的服務可視化和故障智能定位系統
artemis 是我們基於 hulk 研發的一款服務可視化與故障智能定位追蹤系統,它集服務部署架構可視化、近實時多維度監控、關鍵日誌、服務調用鏈等多方面信息,可以快速、高效、精準的發現和定位穩定性問題。
該系統目前已接入好看 / 全民 / 度咔等多個後端服務,極大加速了故障定位效率。在一些故障場景,可以秒級定位問題,給出問題的代碼行。
3.4.1 服務部署架構
通過實例列表,可以獲取服務的 idc 列表、instance 列表和詳情,並提供了便捷高效的調試入口和登錄指令:
3.4.2 近實時多維度監控
artemis 提供的近實時監控,能夠提供更多維度信息,這些維度是 sia 和 prometheus 無法提供的,如:
-
某個 URI 下面的某個下游 (或下游實例)RAL 的 QPS、耗時、可用性;
-
某個服務實例實例的 URI 或 RAL 的監控信息;
3.4.3 關鍵日誌
由於與 hulk 的深度集成,在業務代碼中打印 warning 級別以上的日誌時,artemis 能拿到更多的日誌信息,如 - 各維度信息、調用棧、上下文等:
3.4.4 服務調用鏈
在 hulk 框架的協助下,artemis 還可以獲取到 URI 及 URI 所依賴的 RAL 調用信息,由此可以構建出請求調用鏈,並實時展示調用鏈上的相關 metrics 信息:
不同顏色的鏈路代表不同的可用性:紅色 - 1 個 9 及以下,黃色 - 2 個 9,藍色 - 3 個 9,灰色 - 4 個 9。通過服務調用鏈,可以非常直觀的看到服務裏,哪個接口有問題,還可以看到哪些下游影響了這個接口的可用性。
3.4.5 使用案例
通過與報警系統的聯動,可以在發生報警的第一時間,在 artemis 系統中找到受影響的服務及 URI,確定是否是下游引起,錯誤是什麼,哪一行代碼報了錯等,以下是一個 artemis 的實際應用示例。
四、總結
===========
hulk 雖然是⼀個新的 go 語⾔ web 框架,但不是重複造輪,⽽是站在⼚內和開源軟件的基礎上,結合業務實際開發、部 署、運⾏、運維環境,對這些開源框架和⼯具進⾏取⻓補短、⼆次開發,最終切合實際的業務使⽤場景。同時,圍繞 hulk 初步構建起的 go 生態,爲服務在開發、部署、運行、運維等各個階段都提供了有力支持。
最後,希望短視頻研發部在 go 服務化架構升級 / 研發框架上的⼀些實踐、⽅案和經驗,能夠給有相同架構升級需求、 在 go 項⽬實踐中遇到問題的其他業務線同學⼀些幫助和參考。
五、附錄 (外網不可訪問)
-
框架及使⽤⽂檔:http://hulk-go.baidu-int.com/
-
hulk 底層是基於 GDP2 的,瞭解 gdp 也更有助於瞭解 hulk:http://gdp.baidu-int.com/
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/JdBjyb95U_oijYoszJubEw