天天看點

Go 語言體系下的微服務架構選型:Dubbo-go

作者:阿裡雲雲原生

作者簡介:牛學蔚(GitHub: @justxuewei):Apache Dubbo PMC,對雲原生、中間件、容器等領域有濃厚興趣,活躍在 Dubbo 和 Kata containers 兩個開源項目中。

01 Go 微服務體系發展與選型

随着微服務技術的快速發展,其在各個領域都形成了一系列事實标準,在 Kubernetes 和容器技術加持下,雲原生微服務已經成為了主流解決方案。而 Go 語言作為雲原生領域最受歡迎的開發語言,正被越來越多的企業作為微服務開發的首選語言,其中比較流行的包括 Go-micro、Go-zero、Dubbo-go 等。作為 Dubbo 微服務體系中多語言實作的一員,在 2022 年 Dubbo-go 以微服務領跑者的角色積極擁抱雲原生标準,探索了 Proxyless Mesh 形态,配合适配 Pixiu 雲原生網關,形成了完善的 Dubbo-go 微服務生态矩陣。

以 Dubbo-go 為中心的微服務體系在多個知名企業中成功落地和實踐,架構的穩定性在實際場景下經受住了考驗。截止今年已有 60+ 家企業在我們的使用者清單中登記,其中較為典型案例請參考文章《小米電商 Apache Dubbo-go 微服務實踐》。小米電商選用了 Dubbo-go + Nacos + sidecar + etcd + mirpc 為核心的微服務體系,除了看中了 Dubbo-go 的互聯互通和服務治理能力外,也認可 Dubbo-go 在微服務方向的沉澱和積累。

02 Dubbo-go 簡介

2.1 什麼是 Dubbo-go

Apache Dubbo 是一款易用、高性能的 WEB 和 RPC 架構,同時為建構企業級微服務提供服務發現、流量治理、可觀測、認證鑒權等能力、工具與最佳實踐。Dubbo3 從設計上不綁定程式設計語言,社群目前提供了 Java、Go、Rust、Node.js 等多語言實作,在未來,我們計劃為所有主流語言提供對等的微服務開發體驗。

Dubbo 架構作為國内最具影響力的開源微服務開發架構之一,擁有非常高的關注度和活躍度,在 GitHub 上擁有 3.8 萬+ stars。Dubbo 項目于 2017 年捐贈給 Apache 基金會,在經曆了短短 15 個月孵化後順利畢業,在 Apache 基金會管理的全部項目中關注度排名第三(前兩名分别是 echarts 和 superset),Dubbo-go 作為 Dubbo 多語言生态的重要一員,很好的相容 Dubbo 生态的同時提供面向 Go 語言體系的微服務開發體驗。

Dubbo-go(項目位址 github.com/apache/dubbo-go)作為 Dubbo 多語言生态的重要組成部分,目前完全兌現了 Dubbo3 架構的核心能力,并且在雲原生時代,憑借 Go 語言無需重量級虛拟機、靜态編譯以及垃圾回收的特性,獲得了廣泛關注,其應用規模也逐漸擴大。從特性上來說,Dubbo-go 目前支援 HTTP/2、TCP、gRPC 協定通信、服務發現、流量管控、配置管理、全鍊路追、可視化觀測等諸多新特性,Dubbo3 已是衆多使用者生産環境首選的微服務架構(使用者清單);在生态建設方面,Dubbo-go 适配了包括 Zookeeper、Nacos、Sentinel、Zipkin、Kubernetes、Prometheus、雲原生 API 網關項目 Dubbo-pixiu、異步網絡庫 Dubbo-getty、Hessian2 等生态項目。

2022 年 Dubbo-go 社群以生态互聯、開發者體驗、穩定性為切入點,不斷優化系統架構,社群榮獲多個開源獎項:

  • Dubbo 生态被評為 2021 年中國 20 大最活躍社群之一
  • Dubbo-go 入圍 2021 年“科創中國”榜單。
  • Dubbo-go 開源社群被 OSCHINA 評為“2022 年度 OSCHINA 優秀開源技術團隊”。

2.2 重要特性

通信協定:遵循 Dubbo 核心架構設計,Dubbo-go 在實作上不綁定通信協定,目前支援 HTTP/2、TCP (Dubbo2)、JSONRPC、gRPC、HTTP 等多種通信協定,開發者可以根據使用場景靈活的選擇通信協定。

服務注冊:支援 Client-based 服務發現機制,支援注冊中心适配如 Nacos、Consul、Zookeeper 等。Dubbo3 的服務發現機制誕生于阿裡巴巴超大規模微服務電商叢集實踐場景,其在性能、可伸縮性、易用性等方面的表現大幅領先于業界大多數主流開源産品。

配置中心:Dubbo 配置中心可實作應用配置的遠端托管,支援配置變更的實時感覺,目前支援 Nacos、Apollo(攜程開源)、ZooKeeper 等作為配置中心。

負載均衡:Dubbo 提供了多種負載均衡政策,如随機負載均衡政策、一緻性哈希負載、基于權重的輪詢、最小活躍度優先、自适應負載均衡 P2C 等。

流量控制:Dubbo 的流量管控規則可以基于應用、服務、方法、參數等粒度精準的控制流量走向,基于此可靈活的實作逾時時間調整、開啟通路日志、金絲雀釋出、參數路由、同區域優先、按比例流量分發等。除此之外,通過接入 Hystrix、Sentinel 等,Dubbo-go 還支援自适應限流、限流熔斷等。

分布式事務:支援 Seata-golang 分布式事務架構,實作了 AT 模式和 TCC 模式分布式事務的調用,AT 模式相較 TCC 模式對代碼的入侵性更小、需要開發的接口更少,但 AT 模式對事務操作的資料持有全局鎖,TCC 模型性能更好。

鍊路追蹤:支援基于 Jaeger、ZipKin 的鍊路追蹤能力。

名額可視化:支援使用 Prometheus 收集架構名額和使用者名額。

可擴充性:Dubbo-go 提供了靈活的 extension 擴充機制,使用者可随時根據自己的需求靈活擴充服務發現、負載均衡、配置中心、流量管控規則、全鍊路追蹤等中間件。

03 過去一年我們做了什麼

3.1 優雅上下線

在微服務場景下,業務是以容器的形式對外提供服務,k8s 能夠友善的對 Pod 進行滾動更新,在舊版本被替換的時候應該達到無損下線的效果,即容器不能被銷毀直到沒有正在處理的請求。如果其不能被正确實作,對于承載高流量的線上服務來說,在更新期間可能會導緻大量的請求報錯,甚至可能觸發報警,其影響是巨大的。優雅上下線功能是 Dubbo-go 3.0 正式版本釋出後的第一個重大增強,王曉偉同學(GitHub: @XiaoWeiKIN)貢獻了全流程的優雅上下線能力。

Go 語言體系下的微服務架構選型:Dubbo-go

Dubbo 經典的調用流程如上圖所示,這裡面包含了服務提供者(Provider)、服務消費者(Consumer)以及注冊中心(Registry)三個關鍵元件,一個服務能夠被調用,首先需要提供者準備服務并對外暴露端口(步驟 0),然後提供者需要将調用資訊注冊到注冊中心中(步驟 1),消費者則會通過異步訂閱的方式擷取最新的提供者資料(步驟 2),注冊中心在有新資料後會主動推送給消費者(步驟 3),此時消費者已經有本次調用的全部資訊了,最後消費者發送調用請求(步驟 4),這樣就完成了整個調用鍊路。

在單體應用中,上述邏輯非常清晰和簡單,但是在大規模微服務叢集中,這個邏輯的每一個細節都需要被仔細推敲後,才能保證上下線的過程中調用不出錯。

Go 語言體系下的微服務架構選型:Dubbo-go

優雅上線的目标是解決服務上線調用報錯的問題,主要針對微服務場景下的調用依賴問題。在 Dubbo 生态中,Service 表示一個服務,能夠被暴露并被其他服務調用,Reference 表示引用,可以簡單的了解為下遊服務。一個典型調用結構如上圖所示,該服務對外暴露一個接口,同時引用了下遊的 N 個服務。該服務在上線的時候應該嚴格遵循以下流程,首先保證下遊服務的引用被成功初始化,之後再初始化 Service 對外暴露服務,最後再向注冊中心注冊服務。優雅上線相對來說邏輯比較簡單,隻需要嚴格遵循初始化過程的依賴關系就能保證上線過程中服務能夠被正常調用。

優雅下線是優雅上下線的難點,涉及到了信号監聽、反注冊、等待已有請求完成調用等邏輯。在需要銷毀容器的時候,kubelet 會向容器發送 SIGTERM 信号,Dubbo-go 會進入優雅下線流程,此時容器并不會立刻被銷毀。即将下線的提供者首先會執行反注冊,即向注冊中心中删除自己的資訊,消費者可以通過訂閱獲得這個資訊,這個過程需要一定的時間。換句話說,在消費者獲得這個删除資訊之前,流量還是有可能會流向該提供者,此時提供者應該拒絕這部分請求。當然除了下線期間的新請求外,還有殘留的來自上遊的請求以及自己調用下遊的請求,我們分别為這兩種情況設定一個計數器,當兩個計數器都被清零時,可以認為該提供者是“幹淨”的。Dubbo-go 的政策是拒絕新請求,等待已放行的舊請求。最後,銷毀協定并關閉監聽,該容器就能夠被安全的摘除。

在啟動優雅上下線後,叢集内無錯誤請求,成功率保持在100%。

Go 語言體系下的微服務架構選型:Dubbo-go

3.2 新一代柔性服務

在去年釋出 Dubbo-go 3.0 版本的時候,柔性服務首次作為一個重要特性被提出。時隔一年,我們帶來了全新更新的新一代柔性服務,在新版本中我們将爬山算法替換為峰值幹預算法,在經過多次測試後新算法行為可控性更高、性能更優異,這部分工作由來自北京郵電大學的張業鵬同學(GitHub: @CoolIceV)貢獻。

柔性服務是一種更智能的負載均衡算法。傳統負載均衡算法大多是基于消費者視角,它們共同的局限性是無法根據服務提供者的目前狀态動态調整分流政策,如 RR、hash 等算法。這些算法總是以盡可能公平的機率配置設定流量,但在實踐中公平不等于負載均衡。

爬山算法是一種容量預估的算法,服務提供者需要将一些關鍵資訊回傳給消費者,比如時延、請求排隊數量、預估容量等,消費者使用 P2C 算法選擇一個負載最低的作為本次請求的提供者。這些資料實效性要求非常高,如果這些資料是被動傳遞的,那麼很難保證明效性,如果這些資料是被主動探測的,那麼在一個大型叢集下感覺成本非常高。基于上述問題,我們選擇了更可控的峰值幹預算法。

消費者部分中,我們使用了改良版的 P2C 算法,采集的名額包括請求數(requests)、成功數(accepts)、請求時延(rtt)。與原實作方案不同的是,該版本采用了更合理的滑動視窗(SlidingWindowCounter)和指數移動平均(EMA)兩種帶有時序性的子產品進行采集。

SlidingWindowCounter 會儲存時長為統計周期 T 的資料,整個周期内的資料被分割為若幹個 Bucket,每個Bucket 儲存計數時長内的資料,目前所處的 Bucket 會随着時間前進而向後移動。

Go 語言體系下的微服務架構選型:Dubbo-go

EMA 利用指數移動平均算法進行平滑、減小抖動,适用于統計時延型的名額,計算公式:

Go 語言體系下的微服務架構選型:Dubbo-go

以下為一個用戶端請求 3 個服務端的測試結果,3 個服務端配置不同,分别為 1 核 1GB、2 核 2GB、3 核 3GB。蘭青色虛線代表開始使用上述負載均衡算法,可以看到開啟前每個服務端接收到的請求數幾乎相同,開啟之後流量會根據提供者的規格進行智能分流。

Go 語言體系下的微服務架構選型:Dubbo-go

提供者基于一個 AutoConcurrencyLimiter 元件限流,在請求到達時會判斷已接受的請求是否超過最大處理量,如果超過了就會直接傳回失敗,限流導緻的失敗會影響負載均衡時的成功率,進而影響該執行個體被請求的可能性。與正常限流元件不同的是,該元件會根據采樣情況自動調整服務的最大處理量,不需要手動配置,而且增加了 CPU 負載作為啟動開關,可以減少被錯誤限流的數量。

該元件主要關注 QPS、無負載時延(NoLoadLatency)和最大并發量(maxConcurrency),同時有一個使用者指定的超參數 exploreRatio,表示探索最大并發量的程度。更新規則是

  • 使用總請求數(包含未被采樣的資料)計算 QPS,新 QPS 更大時直接替換,更小則使用指數移動平均進行平滑處理。
  • 使用采樣資料計算平均處理時延來估算 NoLoadLatency,平均時延變小才會更新,并使用指數移動平均進行平滑處理。
  • 增加探索因子啟發式計算 maxConcurrency, 在采樣時延或 QPS 在探索允許的範圍之内時會逐漸增大 exploreRatio,否則用指數移動平均的方式進行平滑處理
Go 語言體系下的微服務架構選型:Dubbo-go

最後可以提供給使用者一個可以通過 cgroup v1 進行 CPU 限制,當目前提供者 CPU 負載過高的時候,會無條件拒絕一切新請求。CPU 限制是一種最壞情況下的兜底政策。

以下為 1 個用戶端請求一個服務端的測試結果,該測試随着時間推移,QPS 會逐漸增大,如藍線所示。可以看到當CPU 負載(橙線)過高時,有請求被限流(黃線),随後即使 QPS 再增大,CPU 負載、請求成功數均已相對穩定。

Go 語言體系下的微服務架構選型:Dubbo-go

3.3 Dubbo Mesh

今年 Dubbo Go 社群釋出了 Dubbo Mesh [1] 架構的完整實作,能夠以 Proxyless Mesh 的形式加入 Istio 服務網格,開啟了 Go 語言體系下的微服務新形态。

Istio 在架構層面分為控制平面(control plane)和資料平面(data plane),其中控制平面是一個名為 istiod 的程序,網絡代理是 envoy 。Istiod 簡體 Kubernetes 資源(resources)擷取服務資訊,比如 Service、Endpoint 等,将這些資訊通過 xDS 協定發送給位于資料平面的 envoy。Envoy 作為一個獨立代理程序以邊車(sidecar)形式運作,該程序與業務程序共同加入同一個網絡,劫持業務流量并轉發到正确的位置。

服務網格能夠屏蔽複雜的服務治理細節,讓開發者能夠專注于業務實作。Istio 通過邊車的形式實作了業務邏輯的無侵入性,降低了系統之間的耦合性,帶來開發便利的同時也引入了轉發時延、額外資源消耗的問題。但是 Istio 作為雲原生時代的标杆産品,其架構模式和思路就有非常大的借鑒意義,針對上述提到的 Proxy Mesh 的弊端,我們提出了一套基于 Dubbo-go 的 Proxyless Mesh 微服務治理模式。

Proxyless Mesh 是無代理服務網格,由 Google 提出後,多個開源産品在這個方向進行了探索和實踐。其核心思路是用 SDK 代替獨立代理程序,SDK 作為資料平面接收來自控制平面的控制資訊,負責服務之間的通信和治理工作。

Dubbo-go 為了融入 Istio 體系,将擴充出來的注冊發現流程進行了特殊改造。除了複用 Istio 提供的 EDS、CDS 主機發現的能力之外,增加了接口名到主機名的映射,作為源資料注冊在了控制平面上。用戶端在發起調用前持有接口名,通過查詢 istiod 上的中繼資料資訊,拿到接口名到主機名到映射,轉換為主機名;再通過 EDS、CDS 和路由,完成主機名到下遊端點執行個體的轉換。完成服務發現流程。

3.4 互聯互通的新典範:Polaris 和 Dubbo-go 全面對接

Dubbo-go 從釋出伊始,一直非常重視與各個開源産品之間的互聯互通,今年我們完成了與 Polaris 全面對接,這部分工作由社群鄧正威同學(GitHub: @jasondeng1997)和春少同學(GitHub: @chuntaojun)貢獻。

Polaris 是一款開源的服務治理平台,緻力于解決分布式和微服務架構中的服務管理、流量管理、配置管理、故障容錯和可觀測性問題,針對不同的技術棧和環境提供服務治理的标準方案和最佳實踐。在經典 Dubbo-go 使用場景下,使用者需要自行部署注冊中心、可觀測服務、流量管理等元件,而 Polaris 内置了服務管理、流量管理、故障容錯、配置管理和可觀測性五大功能,能夠幫助使用者快速降低微服務開發門檻。

Polaris 有統一的控制平面,需要為 Dubbo-go 架構适配相應的資料平面 SDK,使用者隻需要在 Dubbo-go 中開啟 Polaris,架構将自動通過 extension 機制進行注入,開發者無需其他額外的開發工作。從使用者資料流的次元,當使用者在 dubbogo 中啟用 Polaris 的服務治理能力後,業務流量實際處理流程如下:

Go 語言體系下的微服務架構選型:Dubbo-go

目前 Polaris 已實作了 Dubbo-go 原生的服務注冊擴充點,是以原本服務注冊邏輯不需要進行任何調整,隻需要在 dubbogo.yaml 配置檔案中新增 polaris 協定的注冊中心配置即可,如下所示。

dubbo:
  registries:
    demo:
      protocol: polaris
      address: {polaris-ip}:8091           

其中 polaris-ip 表示北極星服務端 IP 位址,此時 Dubbo-go 架構就接入了 Polaris 服務治理架構。Polaris 還提供了流量管理、故障容錯等諸多内容,礙于篇幅限制這裡就不一一展開了,如果有興趣請參閱 《互聯互通的新典範:Polaris 和 Dubbo-go 全面對接》[2]。

3.5 TLS 安全通信支援

在今年我們為 Dubbo 協定、Triple 協定和 gRPC 協定實作了 TLS 安全通信功能,微服務之間能夠以可信的方式調用,該部分由社群張立斌同學(GitHub: @ZLBer)貢獻。

TLS 的前身是 SSL,被用于通信加密,能夠保證傳輸内容不被其他主機檢視和篡改,已經被廣泛的應用于 HTTPS 等技術中。在開啟 TLS 之前,需要生成所需要的證書和秘鑰,我們假設其儲存在 x509 目錄中。啟用 TLS 不需要對業務邏輯進行修改,隻需要設定相應的 Dubbo-go 配置檔案 dubbogo.yaml 即可。

消費者配置檔案:

dubbo:
  consumer:
    references:
      UserProvider:
        url: tri://localhost:20000
        protocol: tri
        serialization: json
        interface: com.apache.dubbogo.samples.rpc.extension.UserProvider
  tls_config:
      ca-cert-file: x509/server_ca_cert.pem
      tls-cert-file: x509/client2_cert.pem
      tls-key-file: x509/client2_key.pem
      tls-server-name: dubbogo.test.example.com           

提供者配置檔案:

dubbo:
  protocols:
    triple:
      name: tri
      port: 20000
  provider:
    services:
      UserProvider:
        serialization: json
        interface: com.apache.dubbogo.samples.rpc.extension.UserProvider
  tls_config:
    ca-cert-file: x509/client_ca_cert.pem
    tls-cert-file: x509/server2_cert.pem
    tls-key-file: x509/server2_key.pem
    tls-server-name: dubbogo.test.example.com           

在正确開啟 TLS 之後,提供者會輸出一條 "Server initialized the TLSConfig configuration" 日志,消費者會輸出一條 "Client initialized the TLSConfig configuration" 日志。更詳細的使用執行個體請參考 tls 示例 [3]。

04 展望 2023

2022 年是極不容易的一年,感謝大家的信任,也非常感謝社群中每位同學的貢獻。展望 2023 我們将會持續打磨架構,在優先保障穩定性的前提下持續提升易用性,打造一流的 Go 語言微服務架構。

4.1 Dubbo 開源整體規劃

Go 語言體系下的微服務架構選型:Dubbo-go
  • 官網與文檔體驗全面提升
  • Go、Node.js、Rust 等多語言體系建設
  • 全面提升整體可觀測性
  • Dubbo Admin 一站式服務運維管控平台
  • Dubbo Mesh 走向成熟
  • 提升 HTTP 開發體驗,補全 Web 互通
  • 打造 gRPC over Dubbo 最佳實踐
  • 完善的認證鑒權體系

4.2 面向 Go 開發者全面使用體驗提升

Dubbo-go 基礎功能建設已經較為完善,同時在最近幾年中我們持續在 dubbo-go-samples 目錄完善示例代碼 [4],能夠幫助使用者更加快速的上手項目。與此同時,Dubbo-go 文檔内容還較為匮乏,代碼示例隻能讓架構跑起來,但是這裡面的使用和實作細節還需要在文檔中進一步被詳細闡述,讓使用者知其然,更知其是以然。除了文檔建設工作外,社群對開發者工具的建設工作高度重視,在 2023 年,我們計劃在 dubboctl 工具中內建更多指令,為使用者提供快速建立微服務、支援了 Hessian2 生成器、建立 Dubbo-go 項目等諸多特性。在 2023 年我們将會進一步提升文檔和工具的建設工作,除了強大的功能之外,成為真正面向 Go 開發者的輕量、易用的架構。

參考連結:

1、Dubbo-go-Mesh 開啟新一代 Go 微服務形态

2、https://baijiahao.baidu.com/s?id=1751764790109427167&wfr=spider&for=pc

3、https://github.com/apache/dubbo-go-samples/tree/master/tls

4、https://github.com/apache/dubbo-go-samples

繼續閱讀