高效能 RPC 框架 CloudWeGo-Kitex 內外統一的開源實踐
日前,位元組跳動技術社群 ByteTech 舉辦的第七期位元組跳動技術沙龍圓滿落幕,本期沙龍以《位元組高效能開源微服務框架:CloudWeGo》為主題。在沙龍中,位元組跳動位元組跳動基礎架構服務框架資深研發工程師楊芮,跟大家分享了《高效能 RPC 框架 Kitex 內外統一的開源實踐》,本文根據分享整理而成。
本文將從以下四個方面介紹 CloudWeGo 高效能 RPC 框架 Kitex 的實踐及開源:
- 由內至外 - 開源過渡;
- 開源一年變更回顧;
- 社群共建完善生態及企業落地;
- 總結和展望。
1. 由內至外 - 開源過渡
很多同學可能剛剛瞭解 CloudWeGo,先介紹一下 CloudWeGo 和 Kitex 的關係。
1.1 CloudWeGo 和 Kitex
Kitex 是 CloudWeGo 開源的第一個微服務框架,它是一個支援多協議的 Golang **RPC 框架,從網路庫、序列化庫到框架的實現基本完全自研的。特別地,Kitex 對 gRPC 協議的支援使用了 gRPC 官方的原始碼,但是我們對 gRPC 的實現做了深度且定製的優化,所以 Kitex 支援的 gRPC 協議效能優於 gRPC 官方框架,效能資料後面會給出對比說明。同時這也是 Kitex 與目前已經開源的支援 gRPC 協議的其他 Golang 框架的主要差異。如果使用者想使用 gRPC 又對效能有很高的要求,那麼 Kitex 框架將會是一個很不錯的選擇。
繼 Kitex 開源後,今年 CloudWeGo 又陸續開源了 Golang HTTP 框架 Hertz ,Rust RPC 框架 Volo,同時圍繞這些微服務框架和微服務的一些通用能力,我們還開源了一些高效能的基礎庫。關於更多 CloudWeGo 開源的子專案,可以進入 CloudWeGo 官網詳細瞭解。
CloudWeGo 官網:http://www.cloudwego.io/
社群同學向我反饋,一些開源群裡大家會討論 Kitex 會不會是一個位元組跳動的開源 KPI 專案呢?它的穩定性、持續效能夠得到保障嗎?我可以負責任地講,Kitex 不是一個 KPI 專案,它是來自位元組跳動內部大規模實踐的真實專案。在 Kitex 開源後始終保持內外統一,基於內外程式碼的統一我們保證了 Kitex 的持續迭代。為了進一步消除大家的顧慮,下面具體介紹一下 Kitex 的誕生和開源歷程。
1.2 Kitex 發展歷史
2014 年,位元組跳動開始引入 Golang。2015 年,位元組跳動內部的服務化開啟。在 RPC 呼叫的場景選擇了 Thrift 協議,內部開始支援 RPC 框架。2016 年,第一個 Golang RPC 框架 Kite 正式釋出。通常在一個公司高速發展的初期,基礎能力都是為了快速支援需求落地,面對的需求場景也較單一,設計上不會有較多考量,其實這也是合理的,因為探索階段並不完全清楚還需要支援哪些場景,過多的考慮反而會出現過度設計的問題。
但是,隨著業務場景複雜化,需求也會多樣化,而且接入服務及呼叫量逐年增長,Kite 已經不足以支援後續的迭代,在線上服役三年多後,2019 年我們開啟了新的專案 Kitex,2020 年初發布了正式版本,在 2020 年底位元組內部已經有 1w+ 服務接入 Kitex。
從 2014 年到 2020 年,Golang 已經是位元組跳動內部主要的業務開發語言,應該是業界 Golang 應用最多的公司,我們的服務框架支援著數萬個 Golang 微服務的可靠通訊,經過數量眾多的微服務和海量流量的驗證,我們已經有了較為成熟的微服務最佳實踐,於是考慮將內部的實踐開源出去豐富雲原生社群的 Golang 產品體系。在 2021年,我們以 CloudWeGo 品牌正式開源了第一個服務框架 Kitex。截止今年 8 月,Kitex 已經為位元組跳動內部 6w+ 的服務提供支援,峰值 QPS 達到上億級別。
大家或許還有疑問,完整的微服務體系離不開基礎的雲生態,無論在公有云、私有云,都需要搭建額外的服務以很好地支援微服務的治理,比如治理平臺、註冊中心、配置中心、監控、鏈路跟蹤、服務網格等,而且還存在一些定製的規範。位元組跳動自然也有完善的內部服務支援微服務體系,但這些服務短期還無法開源,那 CloudWeGo 如何內外維護一套程式碼,統一迭代呢?
關於這個問題,我們看一下 Kitex 的模組劃分。Kitex 的模組分為三個部分:中間是 Kitex 主幹部分 Kitex Core,它定義了框架的層次結構、介面核心邏輯的實現以及介面的預設實現;左邊的 Kitex Tool 則是與生成程式碼相關的實現,我們的生成程式碼工具就是編譯這個包得到的,其中包括 IDL 的解析、校驗、程式碼生成、外掛支援等。不過為了便於使用者使用同時提供更友好的擴充套件,主要能力也做了拆分作為基礎庫獨立開源,如 Thriftgo、Thrift-validator 外掛、Fastpb;右邊的 Kitex Byted 是對位元組內部基礎能力整合的擴充套件實現,我們在開始就將內部的能力作為擴充套件收斂到一個 package 下。
如此就可以將 Kitex Core 和 Tool 部分開源出去,我們將程式碼做了拆分,Kitex 的核心程式碼和工具部分遷移到開源庫,整合內部擴充套件的模組作為 Kitex 的擴充套件保留在內部庫,同時內部庫封裝一層殼保證內部使用者可以無感知地升級。
那麼 Kitex 的開源就只是程式碼拆分這麼簡單嗎?顯然不是。2021年2月,我們開始籌備 Kitex 的開源,雖然基於 Kitex 的擴充套件性,我們可以與內部基礎設施整合的能力解耦,但是 Kitex 仍然依賴內部的一些基礎庫,如果要開源必須先開源基礎庫的能力。所以我們首先做了依賴庫的梳理,與相關的同學合作先開源了 b yte d ance/gopkg 庫。這個庫由 CloudWeGo 與位元組跳動的語言團隊合作維護,裡面包含也了對 Golang 標準庫能力的增強,感興趣的同學可以關注使用。
bytedance/gopkg: http://github.com/bytedance/gopkg
在 gopkg 庫開源後,我們調整程式碼進行開源適配。2021 年 7 月,Kitex 正式開源,在內部發布中版本使用開源庫。但 Kitex 畢竟支援了內部幾萬的微服務,我們必須要確保內部服務在這個變更後可以平滑過渡,所以在開源初我們沒有對外官宣,在確認穩定性後,2021年9月,Kitex 正式對外官宣開源。
介紹完了 Kitex 誕生、開源的歷程,希望能夠消除外部同學關於“Kitex 會不會是一個 KPI 專案?”的顧慮。
1.3 開源的價值
第一部分的最後,簡單講一下開源能為我們帶來的價值。Kitex 不是為了開源而實現的,但它的實現是面向開源的。 Kitex 本身是一個經過內部大規模實現的專案,我們希望 Kitex 開源後能幫助更多使用者在內部快速搭建微服務,同時開源能讓我們收集更多社群和企業的反饋,也能吸引外部開發者共建,促進 Kitex 面向多元場景支援的演進,豐富產品能力,然後能在更多場景和企業得到落地,這是一個正向迴圈,互利共贏的過程。
2. 開源一年變更回顧
2.1 框架的衡量指標
在介紹 Kitex 開源一年變更前,先分享一下框架的衡量指標,這是大家在選擇一個框架時要考慮的。
- 擴充套件性
如果一個框架與內部能力強耦合,就無法移植到其他平臺,或框架的支援場景單一也無法進行擴充套件,這樣的框架很難得到外部的使用。
- 易用性
框架的易用性體現在兩個方面。第一是面向業務開發者,如果一個框架在使用過程中需要讓使用者關注很多框架的細節,那麼對研發效率要求很高的團隊可能無法接受。第二是面向框架的二次開發者,他們需要對框架做一些定製支援,如果框架提供的擴充套件能力過於寬泛,擴充套件成本很高,或者可擴充套件的能力不夠多,那麼這個框架也是存在侷限性的。
- 功能的豐富度
雖然基於擴充套件性可以對框架進行定製,但不是所有開發者都有足夠的精力做定製開發,如果框架本身對各種擴充套件能力提供了不同選擇的支援,對於開發者來說只需要根據自己的基礎設施進行組合就能在自己的環境中執行。
- 高效能
前面三點是初期選擇框架需要重點關注的指標,但隨著服務規模和資源消耗變大,效能就成了不容忽視的問題。從長期的角度來說,選擇框架的時候一定要關注效能,否則後續只能面臨框架替換的問題,或者被迫對這個框架做定製維護。
關於以上四點框架的衡量指標,雖然 Kitex 目前還沒做到最好,但是這四個要素都是 Kitex 設計和實現中一直在兼顧的,我們不會顧此失彼。
2.2 功能特性
下面就幾個開源一年來重要的功能特性進行介紹。
2.2.1 Proxyless
Proxyless 是 Kitex 面向開源場景提供的支援。在 Kitex 開源初期,我們內部討論過是否要支援 xDS 對接 Istio,對於外部使用者來說,使用 Istio 可以快速搭建一套基本的微服務架構,解決服務發現、流量路由、配置下發等問題,但是如果使用完整的 Istio 的解決方案,就要引入 Envoy,這會增加運維成本,而且直接使用官方的 Envoy 方案對效能有損,會引入額外的 CPU 開銷且增加延遲。如果 Kitex 能直接對接 Istio,既能讓使用者享受到部分 Istio 的能力,又可以避免 Envoy 帶來的效能損失和部署運維成本。 但是在開源初期,我們沒有看到很明確的使用者訴求,因此沒有對此做高優的支援。
後來 gRPC 官方也釋出了 Proxyless 的支援,同時 Istio 的官方也將 Proxyless 作為使用 Istio 的一種方式。Kitex 現在也已完成支援,目前主要是對接服務發現,xDS 支援的擴充套件單獨開源到了 kitex-contrib/xds 庫中,後續還會完善。大家可以根據 README 瞭解如何使用 Kitex 對接 Istio。
xDS Support: http://github.com/kitex-contrib/xds
2.2.2 JSON 和 Protobuf 泛化呼叫支援
之前,Kitex 支援了應用在閘道器場景的 HTTP 泛化,以及支援了應用在一些通用服務場景的 Map 和二進位制泛化。開源後,根據使用者的需求反饋又新增了 JSON 和 Protobuf 的泛化。
Protobuf 的泛化也是應用在 API 閘道器的場景。原來的 HTTP 泛化傳輸的資料格式是 JSON,但是 JSON 的序列化體積大、效率低,對效能有影響,所以很多移動端的介面選擇使用 Protobuf 傳輸資料,因此增加了 Protobuf 泛化的支援。
目前 Kitex 的泛化主要針對後端的 Thrift 服務,無論是 Protobuf 、 Map 還是 JSON,Kitex 都會在呼叫端結合 IDL 解析,將這些資料對映編碼為 Thrift 包發給後端服務。
那麼為什麼把泛化放在呼叫端而不是服務端呢?大家廣泛瞭解的泛化都是服務端對泛化請求做了解析處理,當然呼叫端也要相應地提供泛化的 Client。但是泛化面向的是通用服務,泛化使用成本其實是比較高的,它並不適用於普通的 RPC 場景,而通用服務面向的是所有後端的服務,有 Golang/Java/C++/Python/Rust,如果每一種語言框架都支援泛化,成本是非常高的。就算各個語言都對泛化做了支援,框架版本收斂又是一個漫長的過程,對於通用服務來說,對接所有的服務就顯得不太現實。綜合以上原因,泛化放在呼叫端支援。
2.2.3 重試能力增強
去年開源時,Kitex 已經支援了重試功能。之前支援的重試有兩類,一個是超時重試,一個是 Backup Request。
對於超時來重試來說,我們只會對超時這一種異常進行重試,但為了進一步提高請求成功率,使用者希望對其他的異常也進行重試,或者使用者可能會定義一些使用者請求的狀態碼,結合使用者狀態碼進行重試,在這種情況下,顯然我們只支援超時重試是不滿足使用者需求的。基於這個背景,Kitex 新增了 指定結果重試,使用者可以指定其他異常或指定某一類 Response,框架會結合使用者指定的結果進行重試。
其次,使用者在配置重試時,如果通過程式碼配置的方式設定重試,它會對整個 Client 的所有 RPC 方法生效,但是使用者希望針對不同的 RPC 方法應用不同的重試策略,甚至同一個方法也希望可以採用不同的重試策略,因為不同鏈路上發起的同一個方法的請求對指標要求也會不同。比如有些想使用 Backup Request 減少延遲,有些想做異常重試提高成功率,對於這種情況,Kitex 新的版本支援了請求粒度配置重試。
下圖是使用示例。以請求粒度重試配置為例,比如 RPC 方法是 Mock
,那麼我們在發起 RPC 呼叫的時候,在後面可以配置一個 callopt
指定重試策略,此次請求就會使用這個重試策略。
2.2.4 Thrift Validator
Thrift-gen-validator 是 Thriftgo 的一個工具外掛,它可以根據 Thrift IDL 中定義的註解描述約束給對應的 struct
生成 IsValid() error
方法,校驗值的合法性。通常做 RPC 呼叫的時候,使用者可能會對一些欄位校驗合法性,使用者如果直接寫這些校驗程式碼,投入的成本會很高。所以我們就提供了註解支援,只要使用者在 IDL 中根據規範 定義註解,Kitex 就可以幫助使用者生成校驗程式碼。
下圖是程式碼生成的命令和一個 IDL 註解定義示例,在生成程式碼的時候指定 Thrift Validator 的外掛,我們的外掛工具就會解析註解,為使用者生成這一套合法性校驗的程式碼。目前我們也將 Thrift Validator 的功能貢獻給了 Apache Thrift。
2.3 效能優化
介紹完幾個重要的功能特性,再介紹幾個在效能上的優化特性。
2.3.1 Thrift 高效能編解碼
Frugal 是一 個 無需生成編解碼程式碼、 基於 JIT 的高效能動態 Thrift 編解碼器。 雖然我們針對官方 Thrift 編解碼已經做了優化,支援了 FastThrift,這個在我們開源前釋出的優化實踐裡也有介紹,但我們希望能有進一步的效能提升,參考我們開源的高效能 JSON 庫 Sonic 的設計,實現了 Thrift JIT 編解碼器。下圖中的表格是 Frugal 結合 Kitex 與 FastThrift 的效能對比。
可以看到在大部分場景 RPC 效能表現都較優。除了效能上的優勢,Frugal 還有另一個優勢是無需生成編解碼生成程式碼。Thrift 的生成程式碼比 Protobuf 繁重,一個複雜的 IDL 程式碼生成檔案可以達到幾萬行,而這些程式碼本來對使用者來說無需關注,卻需要由使用者來維護。Frugal 只需要生成結構體程式碼,不需生成編解碼程式碼,就大大解決了這個問題。
關於如何在 Kitex 中使用 Frugal,可以參考倉庫的 Readme。當然使用者也可以單獨使用 Frugal 作為 Thrift 高效能編解碼器,Kitex 後續也會考慮預設使用 Frugal。
Frugal: http://github.com/cloudwego/frugal#readme
2.3.2 Protobuf 高效能編解碼
雖然我們內部主要支援 Thrift,但開源之後我們發現外部使用者對於 Protobuf 或 gRPC 的關注會更多,所以參考 Kitex FastThrift 的優化思路,重新實現了 Protobuf 的生成程式碼。在 v0.4.0 版本,如果使用者使用 Kitex 的工具生成 Protobuf 的程式碼,就會預設生成 Fastpb 的編解碼程式碼,在發起 RPC 呼叫的時候,Kitex 也會預設使用 Fastpb。
下圖是 Fastpb 與官方 Protobuf 序列化的效能對比,可以看到無論是編碼還是解碼,在效率和記憶體分配上,Fastpb 都遠遠優於官方 Protobuf 序列化庫。
2.3.3 gRPC 效能優化
開源初期,我們對 gRPC 整體穩定性和效能的關注是比較少的。因為內部使用的場景不是很多。開源後收到了很多外部同學的反饋,所以我們針對 gRPC 做了一個專項的問題治理以及效能優化。今年中旬我們已經把相關的優化正式提交到開源庫,在 v0.4.0 版本釋出。
Kitex v0.4.0: http://mp.weixin.qq.com/s/ezifbQkHcZQP6MygmJABYA
下圖中左側是優化前 Kitex-gRPC 和官方 gRPC 框架對 Unary 請求的壓測吞吐對比,在併發比較低的情況下,Kitex 的吞吐並不具有優勢,使用 Fastpb 的時候,Kitex 的吞吐表現會好一些,但低併發的吞吐依然低於官方 gRPC。在優化之後,吞吐對比如右圖所示。相比優化前吞吐提升 46% - 70% ,相比官方 gRPC 框架,吞吐高 51% - 70% 。
下圖中右側是優化後 Unary 請求的延遲對比,在吞吐比官方 gRPC 高出很多的情況下,Kitex 的延遲也顯著低於官方的 gRPC。同時就 Kitex 自身而言,在優化後延遲表現也好了很多。
我們再看下 Streaming 請求的壓測效能對比,優化前 Streaming 請求的表現同樣在低併發的情況下,相對 gRPC 框架沒有優勢。經過優化後,Kitex 吞吐顯著高於官方 gRPC,如下圖,同時低併發下吞吐高但延遲持平,增加併發後能看到延遲出現分叉。所以在效能上, Kitex 支援的 gRPC 協議相對官方有明顯的優勢。
雖然在部分功能上,Kitex 還沒有完全對齊,但是目前已經可以滿足大部分的場景需求,我們後續也會繼續進行功能對齊。
3. 社群共建完善生態及企業落地
3.1 社群共建的 Kitex 擴充套件生態
開源後,我們很欣慰得到了很多開發者的關注,坦白說內部團隊精力有限,無法快速建立起面向外部使用者的 Kitex 擴充套件生態。但是一年以來藉助社群的力量,Kitex 在服務註冊/發現、可觀測性、服務治理幾部分的擴充套件得到了很多補充,尤其是服務註冊/發現相關的擴充套件,目前開源的主流注冊中心都已完成對接,雖然在功能豐富度上我們還有待加強,但結合已有的支援,對於外部使用者已經具備了搭建微服務架構的能力。
衷心感謝積極參與 CloudWeGo 社群建設的同學們!關於 Kitex 相關的生態支援,大家可以進入 Kitex-contrib 瞭解更多的開源倉庫。
Kitex-contrib: http://github.com/kitex-contrib
3.2 對接外部企業,協助落地
我們開源的初衷是為了助力其他外部企業快速地搭建企業級的雲原生架構。開源後,森馬、華興證券、貪玩遊戲、禾多科技先後主動與我們聯絡,反饋使用問題、提出需求,的確讓我們發現了一些和內部場景不一樣的問題,需要我們去關注、支援和優化,我們很開心 Kitex 能在這些企業內部得到應用。在今年 6 月 25 日的 CloudWeGo Meetup 中,森馬和華興證券的研發同學也分享了他們使用 Kitex 的內部實踐。
森馬:http://mp.weixin.qq.com/s/JAurW4P2E3NIduFaVY6jew
華興證券:http://mp.weixin.qq.com/s/QqGdzp-7rTdlxedy6bsXiw
除了以上企業,還有一些公司也私下向我們諮詢過使用問題,我們非常感謝這些企業使用者的支援,以及向我們提出的反饋資訊。如第一部分所講,收集社群和企業的反饋可以促進 Kitex 面向多元場景支援的演進,企業使用者如果有相關需求,歡迎來聯絡我們。
3.3 如何使用 Kitex 與內部基礎設施整合
這裡再簡單介紹下如何使用 Kitex 與大家的內部基礎設施整合。以位元組內部為例,內部倉庫裡有開源庫中的擴充套件實現,整合內部的能力,在 bytedSuite 中,我們針對不同場景對 Kitex 進行初始化。如下面的程式碼示例,使用者只需要在構造 Client 和 Server 時增加一個 option 配置就可以完成整合,不過為了讓使用者完全不需關注內部能力的整合,我們將該配置放在了生成的腳手架程式碼中,關於配置如何內嵌在生成程式碼中,後續我們也會開放出來,方便外部的框架二次開發者能以同樣的方式為業務開發同學提供整合能力。
4. 總結和展望
4.1 總結
本次分享主要介紹了以下內容:
- Kitex 如何保持內外統一地從內部應用較廣的框架轉為開源框架;
- 開源一年以來發布了哪些重要的功能特性,做了哪些效能優化;
- 藉助社群的力量現在 Kitex 的周邊生態如何、企業落地情況以及如何使用 Kitex 優雅地整合內部能力。
4.2 展望
- 與社群同學共建,持續豐富社群生態;
- 結合工程實踐,為微服務開發者提供更多便利;
- 完善好 BDThrift 生態,持續優化 Protobuf/gRPC;
- 更多特性支援或開源,ShmIPC、QUIC、Protobuf 泛化…
5. 位元組跳動 CloudWeGo 團隊
CloudWeGo 是一套由位元組跳動開源的、可快速構建企業級雲原生微服務架構的中介軟體集合。它包含許多元件:Golang RPC 框架 Kitex,HTTP 框架 Hertz,Rust RPC 框架 Volo,網路庫 Netpoll,Go 語言 Thrift 編譯器 Thriftgo 等等。通過結合社群優秀的開源產品和生態,可以快速搭建一套完善的雲原生微服務體系。專案共同的特點是高效能、高擴充套件性、高可靠,專注於微服務通訊與治理。CloudWeGo 專案自 2021 年 9 月開源,至今開源 1 年,獲得 1w+ star,程式碼貢獻者人數已有 140+,年度 Awesome Contributor 提名 84 位。更多生態能力對接,請參考 kitex-contrib 和 hertz-contrib。
以上內容整理自第七期位元組跳動技術沙龍《位元組高效能開源微服務框架:CloudWeGo》,獲取講師 PPT 和回放視訊,請在公眾號“位元組跳動技術團隊”後臺回覆關鍵詞“0827”。
- 解鎖抖音世界盃的畫質優化實踐
- Kafka 架構、核心機制和場景解讀
- 頭條穩定性治理:ARC 環境中對 Objective-C 物件賦值的 Crash 隱患
- 位元組跳動模型大規模部署實戰
- 「飛書績效」寬表SQL自動生成邏輯淺析
- Mybatis原始碼主流程分析
- 推薦系統的Bias
- 抖音 Android 基礎技術大揭祕!| 位元組跳動技術沙龍第十期
- 基於序列標註模型的主動學習實踐
- 加密技術科普
- 二維碼掃描優化
- 前端監控系列4 | SDK 體積與效能優化實踐
- 特效側使用者體驗優化實戰 —— 包體積篇
- 深入理解 Android Studio Sync 流程
- 選擇 Go 還是 Rust?CloudWeGo-Volo 基於 Rust 語言的探索實踐
- 初探自然語言預訓練技術演進之路
- 高效能 RPC 框架 CloudWeGo-Kitex 內外統一的開源實踐
- 開源 1 週年突破 1w Star - CloudWeGo 開源社群實踐分享
- Go 語言官方依賴注入工具 Wire 使用指北
- prompt 綜述