當前位置: 妍妍網 > 碼農

Kitex 支持 Dubbo 協定:助力多語言雲原生生態融合

2024-02-28碼農

文章來源:CloudWeGo 開源社群

0 1


背景


Kitex 是字節跳動基礎架構服務框架團隊推出的 Go 微服務 RPC 框架,支持 Thrift、Kitex Protobuf、gRPC 等訊息協定,具有高效能、強可延伸的特點。於 2021 年 9 月正式開源後,已在多家外部企業成功落地,為他們帶來了真實的成本、效能和穩定性收益。

很多企業使用者在使用 Kitex 改造服務的過程中,需要 Kitex 能與現有的 Dubbo 框架實作的服務進行通訊,這與 CloudWeGo 社群積極拓展生態的目標不謀而合,因此 Dubbo 互通計畫 codec-dubbo 應運而生。

在社群同學的熱情幫助下,目前 codec-dubbo 能做到 Kitex 與 Dubbo-Java,Kitex 與 Dubbo-Go 互通,支持 Dubbo 使用者向 Kitex 遷移。

本文將以方正證券利用 Kitex 與 codec-dubbo 成功進行服務改造為例,對改造過程中使用到的 codec-dubbo 主要功能進行闡述,並簡要分析其中的實作細節。

02


企業落地案例

方正證券原有的服務采用 Java 和 Dubbo 框架編寫,兩者穩定且經過了大量場景的驗證,符合他們的生產和開發需求。以請求量較大的小方個股詳情頁為例,高峰期的介面 QPS 在 3-4k,使用 16 台 16 Core 64G 虛擬機器進行承載。

隨著雲原生架構的興起,憑借記憶體占用與執行效率的優勢以及天然適配雲原生,Go 逐漸成為構建企業服務的重要技術選項。為了更好地降本增效,綜合考慮成本、效能和穩定性等因素後,他們決定在新建套用上由 Java 轉向 Go,引入 Kitex,Hertz 等 CloudWeGo 計畫進行服務開發與重構,並整體遷移至 Kubernetes 環境。

在重構過程中,codec-dubbo 憑借接近原生 Kitex + Thrift 的使用體驗以及對 Dubbo 概念的良好支持,降低了使用和理解成本,成功幫助他們解決了 Kitex <-> Dubbo 的互通問題,讓 Kitex 服務順利呼叫原有的 Dubbo 服務。

目前,使用了 codec-dubbo 的 Kitex 服務已成功上線,穩定執行兩個月。還是以小方個股詳情頁為例,Kitex 和 Hertz 承載了該頁面一半左右的介面,在 QPS 不變的情況下,只需要提供 12 個 4 Core 4G Pod,降低資源占用效果顯著。

03


codec-dubbo 功能特性

Dubbo 協定編解碼器

Dubbo 服務主要使用 Dubbo 協定進行通訊,為了支持 Kitex <-> Dubbo 互通,我們需要在 Kitex 中實作 Dubbo 協定。得益於 Kitex 優秀的擴充套件性,codec-dubbo 根據 Kitex 提供的 Codec 介面實作了 DubboCodec 這一核心編解碼器,只需在初始化時註入 DubboCodec 便能使用 Dubbo 協定。

型別對映與拓展

型別對映

Dubbo 主要使用 Hessian2 序列化協定進行 Payload 的編解碼,它最大的特點是自描述序列化型別,即不依賴外部 Schema 或介面定義。序列化過程依賴程式語言型別和 Hessian2 型別之間的對映,以 Go 型別轉化為 Java 型別為例:

經過分析,我們發現 Hessian2 的基礎型別系統與 Thrift 基本重合。為了保證 Kitex + codec-dubbo 的使用體驗與 Kitex + Thrift 基本一致,我們基於 Thrift IDL 來生成 Kitex Dubbo-Hessian2 腳手架程式碼,此時型別轉化過程如下所示:

參考 Dubbo 官方的 dubbo-go-hessian2 型別對映,codec-dubbo 提供如下型別對映(此處僅包含部份對映,更多註意事項請參考 codec-dubbo Readme):

根據 codec-dubbo 提供的型別對映,我們能很輕松地將 Dubbo 介面定義轉化為 Thrift IDL,並使用 Kitex 命令列工具生成計畫腳手架程式碼,最終註入 DubboCodec 完成 Kitex -> Dubbo 的通訊。以下方 Dubbo 介面定義為例:

對應的 api.thrift 檔如下所示,需要註意到其中的結構體定義都需要加上 Java class Name 的註解,對應 Dubbo 介面定義中的 package + 類名。

使用 Kitex 命令列工具,並指定協定為 Hessian2:

之後初始化 DubboCodec 並將其註入 Kitex ,利用生成程式碼編寫以下 Client 端程式碼即可實作 Kitex -> Dubbo 呼叫:

Kitex + codec-dubbo Server 端流程與 Client 端基本類似,具體例子可參考計畫主頁。

型別拓展

Hessian2 schema-free 的特性導致 Dubbo 的實作「過於靈活」,可以使用任意型別。為了適配 Dubbo Hessian2 的型別使用靈活性,codec-dubbo 支持型別拓展,其中主要包括自訂對映與 Java 常用型別拓展。

  • 自訂對映

  • Java 的基礎型別有與之對應的包裝型別,例如 boolean java.lang.Boolean 。型別對映中預設將 Go 的 bool 型別對映到 Java 的 java.lang.Boolean 型別並不能覆蓋到使用 boolean 的情況。

    為了統一使用者使用體驗,讓他們在 Kitex 側只需使用 bool 型別,我們可以在 Thrift 的方法定義後面加上 hessian.argsType="boolean" 註解,利用 thriftgo 的 IDL 反射功能,提前生成 IDL 元資訊並註入 codec-dubbo,便可以在執行時動態地將預設對映型別 java.lang.Boolean 覆寫成 boolean 。具體 Thrift 定義如下所示:

    boolean java.lang.Boolean 類似,其他 Java 基礎型別和包裝型別也能透過這種方式進行自訂對映,此時 codec-dubbo 提供的完整型別對映如下:

  • java 常用型別拓展
  • 由於 Thrift 型別的局限性,我們無法直接使用 Java 類別庫中提供的常用型別。為此,codec-dubbo 在 codec-dubbo/java 包中維護了 Thrift 不支持的 Java 型別(例如 java.lang.Object java.util.Date )以及與之對應的 java.thrift ,同時借助 thriftgo 提供的 idl-ref 功能,我們可以直接在 Thrift IDL 中參照這些型別並生成相應程式碼。當前的 java.thrift 如下所示:

    為了啟用這些型別,我們需要在 Thrift IDL 中使用 include "java.thrift" 匯入它們,並且在使用 Kitex 命令列工具生成程式碼時添加 -hessian2 java_extension 參數來拉取該拓展包。

    Kitex 命令列工具會自動下載 java.thrift ,你也可以手動下載後放到計畫的根目錄。參照 java.thrift 中型別的 Thrift IDL 範例:

    方法多載

    Go 原生不支持方法多載,只能透過定義多個方法來達到類似多載的效果。為了將 Go 中的多個方法對映到 Java 中的多載方法,與自訂對映一節類似,我們在 Thrift 的方法定義後面加上 JavaMethodName 標簽,借助 thriftgo 的 IDL 反射功能在執行時動態地將 Golang 側原本的方法名覆寫成 JavaMethodName 指定的 Java 側中的多載方法。

    以 Java 側的 EchoMethod 為例:

    我們編寫如下 Thrift 定義,即可完成 Go 與 Java 間的多載方法對映,註意到 JavaMethodName hessian.argsType 可以同時使用:

    例外處理

    codec-dubbo 將 Java 中的異常對映為 Go 中的錯誤,這些錯誤統一實作以下介面:

    根據 Dubbo 官方推薦的例外處理實踐以及企業使用者目前的需求,我們將異常劃分為常見異常與自訂異常,同時兼顧使用者的基礎需求以及可延伸需求。

    常見異常

    codec-dubbo 在 pkg/hessian2/exception 包中提供了 Java 常見的異常,目前支持 java.lang.Exception 。

    常見異常無需 Kitex 命令列工具的支持,直接參照即可,以下是 Client 端提取異常和 Server 端返回異常的範例。

  • Client 端提取異常
  • Server 端返回異常
  • 自訂異常

    Java 中的自訂異常往往會繼承一個基礎異常,這裏以 CustomizedException 為例, CustomizedException 繼承了 java.lang.Exception

    得益於 thriftgo 支持生成巢狀結構體,為了在 Kitex 側定義與之對應的異常,我們在 Thrift 中編寫如下定義:

    註意 exception 欄位的註解 thrift.nested="true" ,它讓 thriftgo 生成巢狀結構體,達到類似繼承的效果。

    和 Java 常用型別擴充套件一樣,需要在使用 kitex 腳手架工具生成程式碼時添加 -hessian2 java_extension 參數來拉取拓展包,生成程式碼如下:

    使用方法與常見異常一致,此處不再贅述。

    服務註冊與發現

    Dubbo 同時提供 介面級 套用級 服務註冊發現模型,根據企業使用者當前的生產環境需要,我們選擇優先實作基於 zookeeper 的 介面級 模型:Dubbo registry-zookeeper。

    與我們熟知的套用級模型不同,介面級模型需要維護介面名 => 服務(不同於微服務,更接近 Handler)的對映關系,一個介面名會對映到多個服務,這些服務可能會存在於同一個行程中。

    考慮到 Dubbo 的介面級服務模型與 Kitex 的服務模型差別較大,且 Dubbo registry-zookeeper 應繫結 codec-dubbo 使用,因此不考慮修改 kitex-contrib 中原有的 registry-zookeeper,讓 dubbo registry-zookeeper 成為 codec-dubbo 的一個子 go module 統一進行開發與維護。

    綜合考慮 Dubbo 介面級服務模型、Kitex API 與使用者的使用體驗,我們提供以下的配置層次:

    1. registry/options.go resolver/options.go 中的 WithServers 和 WithRegistryGroup 函式提供註冊中心級別的配置,分別指定 zookeeper 的地址和這些 zookeeper 所屬的組。使用者使用這些函式生成 Kitex 中 registry.Registry discovery.Resolver 例項。

    2. 服務級別的配置由 client.WithTag server.WithRegistryInfo 進行傳遞, registries/common.go 提供 Tag keys,這些 key 與 Dubbo 中的服務後設資料一一對應。

    resolver 範例

    registry 範例

    04


    總結

    Kitex 支持了 Dubbo 協定,是 CloudWeGo 助力多語言雲原生生態融合的一大步,解決了眾多企業使用者 Java 轉 Go、 Java 與 Go 並存的痛點,歡迎大家試用和接入;如果在使用過程遇到任何問題,可以加入我們的飛書使用者群,或者在 Github 上給我們提反饋。

    計畫地址

    GitHub: https://github.com/cloudwego

    官網: www.cloudwego.io

    點選【 閱讀原文 】檢視