dubbo-go介紹
- 2019 年 11 月 11 日
- 筆記

dubbo-go 的前世今生

dubbo-go 是目前 Dubbo 多語言生態最火熱的項目。dubbo-go 最早的版本應該要追溯到 2016 年,由社區於雨同學編寫 dubbo-go 的初版。當時很多東西沒有現成的輪子,如 Go 語言沒有像 netty 一樣的基於事件的網路處理引擎、 hessian2 協議沒有 Go 語言版本實現,加上當時 Dubbo 也沒有開始重新維護。所以從協議庫到網路引擎,再到上層 dubbo-go ,其實都是從零開始寫的。
在 2018 年,攜程開始做 Go 語言的一些中間件以搭建內部的 Go 語言生態,需要有一個 Go 的服務框架可以與攜程的現有 dubbo soa 生態互通。所以由我負責重構了 dubbo-go 並開源出這個版本。當時調研了很多開源的 Go 語言服務框架,當時能夠支援 hessian2 協議的並跟 Dubbo 可以打通的僅找到了當時於雨寫的 dubbo-go 早期版本。由於攜程對社區版本的 Dubbo 做了挺多的擴展,源於對擴展性的需求我們 Go 語言版本需要一個更易於擴展的版本,加上當時這個版本本身的功能也比較簡單,所以我們找到了作者合作重構了一個更好的版本。經過了大半年時間,在上圖第三階段 19 年 6 月的時候,基本上已經把 dubbo-go 重構了一遍,總體的思路是參考的 Dubbo 整體的程式碼架構,用Go語言完全重寫了一個完整的具備服務端跟消費端的 Golang rpc/ 微服務框架。
後來我們將重構後的版本 dubbo-go 1.0 貢獻給 Apache 基金會,到現在已經過去了兩個多月的時間,近期社區發布了1.1版本。目前為止,已經有包括攜程在內的公司已經在生產環境開始了試用和推廣。
Start dubbo-go
現在的 dubbo-go 已經能夠跟 Java 版本做比較好的融合互通,同時 dubbo-go 自身也是一個完成的 Go 語言 rpc/ 微服務框架,它也可以脫離 java dubbo 來獨立使用。
這邊簡單介紹一下用法,寫一個 hello world 的例子。

上圖是一個簡單的 java service ,註冊為一個 Dubbo 服務,是一個簡單的獲取用戶資訊的例子。

上圖是 dubbo-go 的客戶端,來訂閱和調用這個 Java 的 Dubbo 服務。Go 語言客戶端需要顯式調用 SetConsumerService 來註冊需要訂閱的服務,然後通過調用 dubbo-go-hessian2 庫的 registerPOJO 方法來註冊 user 對象,做 Java 和 Go 語言之間的自定義 pojo 類型轉換。具體的服務調用方法就是聲明一個的 GetUser 閉包,便可直接調用。

上圖,同樣的可以基於 dubbo-go 發布一個 GetUser 的服務端,使用方式類似,發布完後可以被 dubbo java 的客戶端調用。

如上圖所示,現在已經做到了這樣一個程度,同樣一份 dubbo-go 客戶端程式碼,可以去調用 dubbo-go 的服務端,也可以去調用 Dubbo Java 的服務端;同樣一份 dubbo-go 的服務端程式碼,可以被 dubbo-go 客戶端和 Java 客戶端調用,所以基本上使用 Dubbo 作為 PPC 框架的 Go 語言應用跟 Java 應用是沒有什麼阻礙的,是完全的跨語言 RPC 調用。更重要的是 dubbo-go 繼承了 Dubbo 的許多優點,如易於擴展、服務治理功能強大,大家在用 Go 語言開發應用的過程中,如果也遇到類似需要與 Dubbo Java 打通的需求,或者需要找一個服務治理功能完備的 Go 微服務框架,可以看下我們 dubbo-go 項目。
dubbo-go 的組成項目
下面介紹一下 dubbo-go 的組成項目,為了方便可以被其他項目直接復用, dubbo-go 拆分成了多個項目,並全部以 Apache 協議開源。
apache/dubbo-go
dubbo-go 主項目, Dubbo 服務端、客戶端完整 Go 語言實現。
apache/dubbo-go-hession2
目前應用最廣泛,與 Java 版本兼容程度最高的 hessian2 協議 Go 語言實現,已經被多個 GolangRPC & Service Mesh 項目使用。
dubbo-go/getty
dubbo-go 非同步網路 I/O 庫,將網路處理層解耦。
dubbo-go/gost
基本類庫,定義了 timeWheel、hashSet、taskPool 等。
dubbo-go/dubbo-go-benchmark
用於對 dubbo-go 進行簡單的壓力測試,性能測試。
apache/dubbo-go-hessian2

先簡單介紹一下 dubbo-go-hessian2 項目。該項目就是 hessian2 協議的 Go 語言實現,最基本的可以將 Java 的基本數據類型和複雜數據類型(如一些包裝類和list介面實現類)與 golang 這邊對應。
詳情可以參考:
https://github.com/hessian-group/hessian-type-mapping
另外 Dubbo Java 服務端可以不捕獲異常,將異常類通過 hession2 協議序列化通過網路傳輸給消費端,消費端進行反序列化對該異常對象並進行捕獲。我們經過一段時間的整理,目前已經支援在 Go 消費端定義對應 Java 的超過 40 種 exception 類,來實現對 Java 異常的捕獲,即使用 dubbo-go 也可以做到直接捕獲 Java 服務端拋出的異常。
另外對於 Java 端 BigDecimal 高精度計算類的支援。涉及到一些金融相關的計算會有類似的需求,所以也對這個類進行了支援。
其他的,還有映射 java 端的方法別名,主要的原因是 Go 這邊語言的規約,需要被序列化的方法名必須是首字母大寫。而 Java 這邊沒有這種規範,所以我們加了一個 hessian 標籤的支援,可以允許用戶手動映射 Java 端的方法名稱。
基本上現在的 dubbo-go 已經滿足絕大多數與 Java 的類型互通需求,我們近期也在實現對 Java 泛型的支援。
dubbo-go/getty

Go 語言天生就是一個非同步網路 I/O 模型,在 linux 上 Go 語言寫的網路伺服器也是採用的 epoll 作為最底層的數據收發驅動,這跟 java 在 linux 的 nio 實現是一樣的。所以 Go 語言的網路處理天生就是非同步的。我們需要封裝的其實是基於 Go 的非同步網路讀寫以及之後的處理中間層。getty 將網路數據處理分為三層,入向方向分別經過對網路 i/o 封裝的 streaming 層、根據不同協議對數據進行序列化反序列化的 codec 層,以及最後數據上升到需要上層消費的 handler 層。出向方向基本與入向經過的相反。每個鏈接的 IO 協程是成對出現的,比如讀協程負責讀取、 codec 邏輯然後數據到 listener 層,然後最後的事件由業務協程池來處理。
該項目目前是與 dubbo-go 解耦出來的,所以大家如果有類似需求可以直接拿來用,目前已經有對於 tcp/udp/websocket 的支援。
Apache / dubbo-go

dubbo-go 主項目,我們重構的這一版主要是基於 Dubbo 的分層程式碼設計,上圖是 dubbo-go 的程式碼分層。基本上與 Java 版本 Dubbo 現有的分層一致,所以 dubbo-go 也繼承了 Dubbo 的一些優良特性,比如整潔的程式碼架構、易於擴展、完善的服務治理功能。
我們攜程這邊,使用的是自己的註冊中心,可以在 dubbo-go 擴展機制的基礎上靈活擴展而無需去改動 dubbo-go 的源程式碼。
dubbo-go 的功能介紹
dubbo-go 已實現功能
目前 dubbo-go 已經實現了 Dubbo 的常用功能(如負責均衡、集群策略、服務多版本多實現、服務多註冊中心多協議發布、泛化調用、服務降級熔斷等),其中服務註冊發現已經支援 zookeeper/etcd/consul/nacos 主流註冊中心。這裡不展開詳細介紹,目前 dubbo-go 支援的功能可以查看項目 readme 中的 feature list ,詳情參考:https://github.com/apache/dubbo-go#feature-list
目前社區正在開發中的功能,主要是早期用戶使用過程中提出的一些需求,也是生產落地一些必需的需求,如監控、調用鏈跟蹤以及服務路由、動態配置中心等更高級的服務治理需求。
dubbo-go 功能介紹之泛化調用

這裡詳細做幾個重點功能的介紹。首先是泛化調用,如上圖,這個也是社區同學提的需求。該同學公司內部有很多 Dubbo 服務,他們用 Go 做了一個 api gateway 網關,想要把 Dubbo 服務暴露成外網 http 介面。因為內部的 Dubbo 服務比較多,不可能每一個 Dubbo 服務都去做一個消費端介面去做適配,這樣的話一旦服務端改動,客戶端也要改。所以他這邊的思路是做基於 dubbo-go 做泛化調用, api-gateway 解析出外網請求的地址,解析出想要調用的 Dubbo 服務的目標。基於dubbo-go consumer 泛化調用指定 service、method ,以及調用參數。
具體的原理是, dubbo-go 這邊作為消費端,實際會通過本地 genericService.invoke 方法做代理,參數裡面包含了 service name,method name ,還包含被調用目標 service 需要的參數類型、值等數據,這些數據後面會通過 dubbo-go-hession2 做轉換,會將內容轉化成 map 類型,經過網路發送到對應的 Java 服務端,然後 Java 那邊是接收的 map 類型的參數,會自動反序列化成自己的 pojo 類型。這樣就實現了 dubbo-go 作為客戶端,泛化調用 Dubbo 服務端的目的。
dubbo-go 功能介紹之降級熔斷

降級熔斷這邊是基於的是大家比較熟悉的 hystrix 的 Go 語言版本,基於 hystrix ,用戶可以定義熔斷規則和降級觸發的程式碼段。降級熔斷支援是作為一個獨立的 dubbo-go filter ,可以靈活選擇是否啟用,如果不啟用就可以在打包的時候不將依賴引入。Filter 層是 dubbo-go 中對於請求鏈路的一個責任鏈模式抽象,目前有許多功能都是基於動態擴展 filter 鏈來實現的,包括 trace、leastactive load balacne、log 等。降級熔斷設計成一個服務調用端獨立的filter可以靈活滿足調用端視角對於微服務架構中「防雪崩「的服務治理需求。
dubbo-go 功能介紹之動態配置
關於動態配置中心, Dubbo 的 2.6 到 2.7 版本做了一個比較大的變化,從之前的 url 配置形式過渡到了支援配置中心 yaml 格式配置的形式,治理粒度也從單服務級別的配置支援到了應用級別的配置,不過在2.7版本中還是兼容 2.6 版本 url 形式進行服務配置的。dubbo-go 這邊考慮到跟 Dubbo2.6 和 2.7 的互通性,同樣支援 url 和配置文件方式的服務配置,同時兼容應用級別和服務級別的配置,跟 dubbo 保持一致,目前已經實現了zookeeper和apollo作為配置中心的支援。
dubbo-go roadmap 2019-2020

最後是大家比較關注的,社區關於 dubbo-go 2019 年下半年的計劃,目前來看主要還是現有功能的補齊和一些問題的修復,我們的目標就是首先做到 Java 和 Go 在運行時的兼容互通和功能的一致,其次是查漏補缺 dubbo-go 作為一個完整 Go 語言微服務框架在功能上的可以改進之處。
另外值得關注的一點是,預計今年年底, dubbo-go 會發布一個支援 kubernetes 作為註冊中心的擴展,積極擁抱雲原生生態。關於雲原生的支援,社區前期做了積極的工作,包括討論關於 dubbo-go 與 Service Mesh 的關係以及在其中的定位,可以肯定的是, dubbo-go 將會配合 Dubbo 社區在 Service Mesh 方向的規劃並扮演重要角色,我們初步預計會在明年給出與 Service Mesh開源社區項目集成的方案,請大家期待。
dubbo-go 社區目前屬於快速健康成長狀態,從捐贈給 Apache 後的不到3個月的時間裡,吸引了大批量的活躍開發者和感興趣的用戶,歡迎各位同道在使用或者學習中遇到問題能夠來社區討論或者給予指正,也歡迎對 dubbo-go 有潛在需求或者對 dubbo-go 感興趣的同道能加入到社區中。
作者資訊:
何鑫銘,目前就職於攜程,基礎中台研發部技術專家,dubbo-go 共同發起人、主要作者,Apache Dubbo committer,關注互聯網中台以及中間件領域。