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 這邊對應。
詳情可以參考
這裡。
另外 Dubbo Java 服務端可以不捕獲異常,将異常類通過 hession2 協定序列化通過網絡傳輸給消費端,消費端進行反序列化對該異常對象并進行捕獲。我們經過一段時間的整理,目前已經支援在 Go 消費端定義對應 Java 的超過 40 種 exception 類,來實作對 Java 異常的捕獲,即使用 dubbo-go 也可以做到直接捕獲 Java 服務端抛出的異常。
另外對于 Java 端 BigDecimal 高精度計算類的支援。涉及到一些金融相關的計算會有類似的需求,是以也對這個類進行了支援。
其他的,還有映射 java 端的方法别名,主要的原因是 Go 這邊語言的規約,需要被序列化的方法名必須是首字母大寫。而 Java 這邊沒有這種規範,是以我們加了一個 hessian 标簽的支援,可以允許使用者手動映射 Java 端的方法名稱。
基本上現在的 dubbo-go 已經滿足絕大多數與 Java 的類型互通需求,我們近期也在實作對 Java 泛型的支援。
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,關注網際網路中台以及中間件領域。