Proxyless Mesh 在 Dubbo 中的實踐

来源:https://www.cnblogs.com/apache-dubbo/archive/2023/01/03/17021257.html
-Advertisement-
Play Games

初識Java Java的特性和優勢 簡單性 就是c++語法的純凈版。沒有頭文件,指針運算,結構,聯合,操作符重載,虛基類等等。 面向對象 面向對象是一種程式設計技術,他將重點放在數據(即對象)和對象之間的介面上。萬物皆對象! 可移植性 這是JAVA的一個重要的優勢。JAVA代碼或者說位元組碼、二進位碼 ...


背景

隨著 Dubbo 3.1 的 release,Dubbo 在雲原生的路上又邁出了重要的一步。在這個版本中添加了 Proxyless Mesh 的新特性,Dubbo Proxyless Mesh 直接實現 xDS 協議解析,
實現 Dubbo 與 Control Plane 的直接通信,進而實現控制面對流量管控、服務治理、可觀測性、安全等的統一管控,規避 Sidecar 模式帶來的性能損耗與部署架構複雜性。

什麼是Service Mesh

Service Mesh 又譯作 “服務網格”,作為服務間通信的基礎設施層。Buoyant 公司的 CEO Willian Morgan 在他的這篇文章 WHAT’S A Service Mesh? AND WHY DO I NEED ONE?
中解釋了什麼是 Service Mesh,為什麼雲原生應用需要 Service Mesh。

下麵是 Willian Morgan 對 Service Mesh 的解釋。

A Service Mesh is a dedicated infrastructure layer for handling service-to-service communication. 
It’s responsible for the reliable delivery of requests through the complex topology of services 
that comprise a modern, cloud native application. In practice, the Service Mesh is typically implemented 
as an array of lightweight network proxies that are deployed alongside application code, without the 
application needing to be aware.

翻譯成中文

服務網格(Service Mesh)是處理服務間通信的基礎設施層。它負責構成現代雲原生應用程式的複雜服務拓撲來可靠地交付請求。
在實踐中,Service Mesh 通常以輕量級網路代理陣列的形式實現,這些代理與應用程式代碼部署在一起,對應用程式來說無需感知代理的存在。

說到 Service Mesh 一定離不開 Sidecar 經典架構模式。它通過在業務 Pod 中註入 Sidecar 容器,接管業務容器的通信流量,同時 Sidecar 容器與網格平臺的控制平面對接,
基於控制平面下發的策略,對代理流量實施治理和管控,將原有服務框架的治理能力下層到 Sidecar 容器中,從而實現了基礎框架能力的下沉,與業務系統解耦。

Service Mesh

經典的 Sidecar Mesh 部署架構有很多優勢,如平滑升級、多語言、業務侵入小等,但也帶來了一些額外的問題,比如:

  • Proxy 帶來的性能損耗,在複雜拓撲的網路調用中將變得尤其明顯
  • 流量攔截帶來的架構複雜性
  • Sidecar 生命周期管理
  • 部署環境受限,並不是所有環境都滿足 Sidecar 流量攔截條件

針對 Sidecar Mesh 模型的問題,Dubbo 社區自很早之前就做了 Dubbo 直接對接到控制面的設想與思考,併在國內開源社區率先提出了 Proxyless Mesh 的概念,當然就 Proxyless 概念的說法而言,最開始是谷歌提出來的。

Dubbo Proxyless Mesh

Dubbo Proxyless 模式是指 Dubbo 直接與 Istiod通信,通過 xDS協議實現服務發現和服務治理等能力。

Proxyless

Proxyless 模式使得微服務又回到了 2.x 時代的部署架構,同 Dubbo 經典服務治理模式非常相似,所以說這個模式並不新鮮, Dubbo 從最開始就是這樣的設計模式。
這樣做可以極大的提升應用性能,降低網路延遲。有人說這種做法又回到了原始的基於 SDK 的微服務模式,其實非也,它依然使用了 Envoy 的 xDS API,
但是因為不再需要嚮應用程式中註入 Sidecar 代理,因此可以減少應用程式性能的損耗。

但相比於 Mesh 架構,Dubbo 經典服務治理模式並沒有強調控制面的統一管控,而這點恰好是 Service Mesh 所強調的,強調對流量、可觀測性、證書等的標準化管控與治理,也是 Mesh 理念先進的地方。

在 Dubbo Proxyless 架構模式下,Dubbo 進程將直接與控制面通信,Dubbo 進程之間也繼續保持直連通信模式,我們可以看出 Proxyless 架構的優勢:

  • 沒有額外的 Proxy 中轉損耗,因此更適用於性能敏感應用
  • 更有利於遺留系統的平滑遷移
  • 架構簡單,容易運維部署
  • 適用於幾乎所有的部署環境

服務發現

xDS 接入以註冊中心的模式對接,節點發現同其他註冊中心的服務自省模型一樣,對於 xDS 的負載均衡和路由配置通過 ServiceInstance 的動態運行時配置傳出,
在構建 Invoker 的時候將配置參數傳入配置地址。

服務發現

證書管理

零信任架構下,需要嚴格區分工作負載的識別和信任,而簽發 X.509 證書是推薦的一種認證方式。在 Kubernetes 集群中,服務間是通過 DNS 名稱互相訪問的,而網路流量可能被 DNS 欺騙、BGP/路由劫持、ARP 欺騙等手段劫持,為了將服務名稱(DNS 名稱)與服務身份強關聯起來,Istio 使用置於 X.509 證書中的安全命名機制。SPIFFE是 Istio 所採用的安全命名的規範,它也是雲原生定義的一種標準化的、可移植的工作負載身份規範。

Secure Production Identity Framework For Everyone (SPIFFE) 是一套服務之間相互進行身份識別的標準,主要包含以下內容:

  • SPIFFE ID 標準,SPIFFE ID 是服務的唯一標識,具體實現使用 URI 資源標識符
  • SPIFFE Verifiable Identity Document (SVID) 標準,將 SPIFFE ID 編碼到一個加密的可驗證的數據格式中
  • 頒發與撤銷 SVID 的 API 標準(SVID 是 SPIFFE ID 的識別憑證)

SPIFFE ID 規定了形如 spiffe://<trust domain>/<workload identifier> 的 URI 格式,作為工作負載(Workload)的唯一標識。
而 Istio 在自身的生態中只使用到了 SPIFFE ID 作為安全命名,其數據格式由自己實現,通信格式採用 CNCF 支持的 xDS 協議規範(證書認證通信更具體來說是 xDS 的 SDS)。

Istio 使用形如 spiffe://<trust_domain>/ns/<namespace>/sa/<service_account> 格式的 SPIFFE ID 作為安全命名,註入到 X.509 證書的 subjectAltName 擴展中。
其中"trust_domain"參數通過 Istiod 環境變數TRUST_DOMAIN 註入,用於在多集群環境中交互。

以下是 Dubbo Proxyless Mesh 證書頒發的過程

證書頒發

  • 創建 RSA 私鑰(Istio 還不支持 ECDSA 私鑰)
  • 構建 CSR(Certificate signing request)模板
  • 自簽名 CSR 生成證書
  • 創建 Kubernetes Secret 資源儲存 CA 證書和私鑰(CA Service處理)

案例實踐

接下來我將帶領大家通過一個例子使已有的項目快速跑在 Proxyless Mesh 模式下。

環境準備

安裝docker

https://www.docker.com/

安裝minikube

牆裂推薦:https://kubernetes.io/zh-cn/docs/tutorials/hello-minikube/

安裝istio

https://istio.io/latest/docs/setup/getting-started/

❗❗❗ 安裝 Istio 的時候需要開啟 first-party-jwt 支持(使用 istioctl 工具安裝的時候加上 --set values.global.jwtPolicy=first-party-jwt 參數),否則將導致客戶端認證失敗的問題。
參考命令如下:

curl -L https://istio.io/downloadIstio | sh -
cd istio-1.xx.x
export PATH=$PWD/bin:$PATH
istioctl install --set profile=demo --set values.global.jwtPolicy=first-party-jwt -y

代碼準備

xds-provider

定義一個介面

public interface GreetingService {

    String sayHello(String name);
}

實現對應的介面

@DubboService(version = "1.0.0")
public class AnnotatedGreetingService implements GreetingService {

    @Override
    public String sayHello(String name) {
        System.out.println("greeting service received: " + name);
        return "hello, " + name + "! from host: " + NetUtils.getLocalHost();
    }
}

編寫啟動類

public class ProviderBootstrap {

    public static void main(String[] args) throws Exception {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ProviderConfiguration.class);
        context.start();
        System.out.println("dubbo service started");
        new CountDownLatch(1).await();
    }

    @Configuration
    @EnableDubbo(scanBasePackages = "org.apache.dubbo.samples.impl")
    @PropertySource("classpath:/spring/dubbo-provider.properties")
    static class ProviderConfiguration {
    }
}

編寫配置信息

dubbo.application.name=dubbo-samples-xds-provider
# 由於 Dubbo 3 應用級服務發現的元數據無法從 istio 中獲取,需要走服務自省模式。
# 這要求了 Dubbo MetadataService 的埠在全集群的是統一的。
dubbo.application.metadataServicePort=20885
# 走xds協議
dubbo.registry.address=xds://istiod.istio-system.svc:15012
dubbo.protocol.name=tri
dubbo.protocol.port=50051
# 對齊k8s pod生命周期,由於 Kubernetes probe 探活機制的工作原理限制,
# 探活請求的發起方不是 localhost,所以需要配置 qosAcceptForeignIp 參數開啟允許全局訪問
dubbo.application.qosEnable=true
dubbo.application.qosAcceptForeignIp=true

編寫Deployment.yml和Service.yml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: dubbo-samples-xds-provider
  namespace: dubbo-demo
spec:
  replicas: 3
  selector:
    matchLabels:
      app: dubbo-samples-xds-provider
  template:
    metadata:
      labels:
        app: dubbo-samples-xds-provider
    spec:
      containers:
        - name: server
          image: apache/dubbo-demo:dubbo-samples-xds-provider_0.0.1
          livenessProbe:
            httpGet:
              path: /live
              port: 22222
            initialDelaySeconds: 5
            periodSeconds: 5
          readinessProbe:
            httpGet:
              path: /ready
              port: 22222
            initialDelaySeconds: 5
            periodSeconds: 5
          startupProbe:
            httpGet:
              path: /startup
              port: 22222
            failureThreshold: 30
            periodSeconds: 10
apiVersion: v1
kind: Service
metadata:
  name: dubbo-samples-xds-provider
  namespace: dubbo-demo
spec:
  clusterIP: None
  selector:
    app: dubbo-samples-xds-provider
  ports:
    - name: grpc
      protocol: TCP
      port: 50051
      targetPort: 50051

編寫Dockerfile

FROM openjdk:8-jdk
ADD ./target/dubbo-samples-xds-provider-1.0-SNAPSHOT.jar dubbo-samples-xds-provider-1.0-SNAPSHOT.jar
CMD java -jar -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=31000 /dubbo-samples-xds-provider-1.0-SNAPSHOT.jar

xds-consumer

定義一個介面

public interface GreetingService {

    String sayHello(String name);
}

實現對應的介面

@Component("annotatedConsumer")
public class GreetingServiceConsumer {
  	// 這裡特別註意的是、由於當前 Dubbo 版本受限於 istio 的通信模型無法獲取介面所對應的應用名,
		// 因此需要配置 providedBy 參數來標記此服務來自哪個應用。
    @DubboReference(version = "1.0.0", providedBy = "dubbo-samples-xds-provider")
    private GreetingService greetingService;

    public String doSayHello(String name) {
        return greetingService.sayHello(name);
    }
}

編寫啟動類

public class ConsumerBootstrap {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConsumerConfiguration.class);
        context.start();
        GreetingServiceConsumer greetingServiceConsumer = context.getBean(GreetingServiceConsumer.class);
        while (true) {
            try {
                String hello = greetingServiceConsumer.doSayHello("xDS Consumer");
                System.out.println("result: " + hello);
                Thread.sleep(100);
            } catch (Throwable t) {
                t.printStackTrace();
            }
        }
    }

    @Configuration
    @EnableDubbo(scanBasePackages = "org.apache.dubbo.samples.action")
    @PropertySource("classpath:/spring/dubbo-consumer.properties")
    @ComponentScan(value = {"org.apache.dubbo.samples.action"})
    static class ConsumerConfiguration {

    }
}

編寫配置信息

dubbo.application.name=dubbo-samples-xds-consumer
dubbo.application.metadataServicePort=20885
dubbo.registry.address=xds://istiod.istio-system.svc:15012
dubbo.consumer.timeout=3000
dubbo.consumer.check=false
dubbo.application.qosEnable=true
dubbo.application.qosAcceptForeignIp=true

編寫Deployment.yml和Service.yml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: dubbo-samples-xds-consumer
  namespace: dubbo-demo
spec:
  replicas: 2
  selector:
    matchLabels:
      app: dubbo-samples-xds-consumer
  template:
    metadata:
      labels:
        app: dubbo-samples-xds-consumer
    spec:
      containers:
        - name: server
          image: apache/dubbo-demo:dubbo-samples-xds-consumer_0.0.1
          livenessProbe:
            httpGet:
              path: /live
              port: 22222
            initialDelaySeconds: 5
            periodSeconds: 5
          readinessProbe:
            httpGet:
              path: /ready
              port: 22222
            initialDelaySeconds: 5
            periodSeconds: 5
          startupProbe:
            httpGet:
              path: /startup
              port: 22222
            failureThreshold: 30
            periodSeconds: 10
apiVersion: v1
kind: Service
metadata:
  name: dubbo-samples-xds-consumer
  namespace: dubbo-demo
spec:
  clusterIP: None
  selector:
    app: dubbo-samples-xds-consumer
  ports:
    - name: grpc
      protocol: TCP
      port: 50051
      targetPort: 50051

編寫Dockerfile

FROM openjdk:8-jdk
ADD ./target/dubbo-samples-xds-consumer-1.0-SNAPSHOT.jar dubbo-samples-xds-consumer-1.0-SNAPSHOT.jar
CMD java -jar -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=31000 /dubbo-samples-xds-consumer-1.0-SNAPSHOT.jar

✅ 到目前為止我們的環境和代碼就全都準備完畢了!

構建鏡像

1、啟動docker

啟動docker

2、啟動minikube

因為minikube是一個本地的k8s,他啟動需要一個虛擬引擎,這裡我們用docker來管理。我們通過如下命令啟動

minikube start

啟動minikube

我們可以在docker里看到minikube

minikube

3、檢查istio的狀態

istio的狀態

4、構建鏡像

在本地找到代碼所在位置、依次執行以下命令

# 找到provider所在路徑
cd ./dubbo-samples-xds-provider/
# 構建provider的鏡像
docker build -t apache/dubbo-demo:dubbo-samples-xds-provider_0.0.1 .

構建provider

# 找到consumer所在路徑
cd ../dubbo-samples-xds-consumer/
# 構建consumer的鏡像
docker build -t apache/dubbo-demo:dubbo-samples-xds-consumer_0.0.1 .

構建consumer

5、檢查本地鏡像

檢查本地鏡像

6、創建namespace

# 初始化命名空間
kubectl apply -f https://raw.githubusercontent.com/apache/dubbo-samples/master/dubbo-samples-xds/deploy/Namespace.yml

# 切換命名空間
kubens dubbo-demo

如果不創建namespace,那麼會看到如下錯誤

錯誤

部署容器

# 找到provider所在路徑
cd ./dubbo-samples-xds-provider/src/main/resources/k8s
# dubbo-samples-xds/dubbo-samples-xds-provider/src/main/resources/k8s/Deployment.yml
# dubbo-samples-xds/dubbo-samples-xds-provider/src/main/resources/k8s/Service.yml

# 部署provider的Deployment和Service
kubectl apply -f Deployment.yml
kubectl apply -f Service.yml

部署provider

# 找到consumer所在路徑
cd ../../../../../dubbo-samples-xds-consumer/src/main/resources/k8s
# dubbo-samples-xds/dubbo-samples-xds-consumer/src/main/resources/k8s/Deployment.yml

# 部署consumer的Deployment
kubectl apply -f Deployment.yml

部署provider

在minikube dashboard看到我們已經部署的pod

部署provider

觀察consumer效果

kubectl logs xxx

result: hello, xDS Consumer! from host: 172.17.0.5
result: hello, xDS Consumer! from host: 172.17.0.5
result: hello, xDS Consumer! from host: 172.17.0.6
result: hello, xDS Consumer! from host: 172.17.0.6

總結&展望

本文主要剖析了 Dubbo Proxyless Mesh 的架構、服務發現以及證書管理等核心流程,最後通過示例給大家演示瞭如何使用 Dubbo Proxyless。

部署provider

隨著 Dubbo 3.1 的 release,Dubbo 在雲原生的路上又邁出了重要的一步。在今年年底,Dubbo Mesh 將發佈具有服務發現能力的版本,
屆時將面向所有 Dubbo 用戶提供從低版本平滑遷移到 Mesh 架構的能力;在明年年初春季的時候將發佈帶有治理能力的版本;在明年年底前發佈帶熱插件更新能力的版本,
希望有興趣見證 Dubbo 雲原生之路的同學可以積极參与社區貢獻!

歡迎在 https://github.com/apache/dubbo 給 Dubbo Star。
搜索關註官方微信公眾號:Apache Dubbo,瞭解更多業界最新動態,掌握大廠面試必備 Dubbo 技能


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 有問必答 最近有好多讀者私信我,為什麼選擇GoFrame做電商項目的開發? 原因很簡單: 因為我司是用GoFrame做電商業務開發的,而且我司同事基本都是PHP轉Go的。GoFrame可以說是非常適合PHPer轉Gopher的開發框架。 在入職我司之前,我有使用Gin和go-micro框架,目前也正 ...
  • JavaSE:基礎語法 註釋 Java中的註釋有三種: 單行註釋:只能註釋當前行,以//開始,直到行結束 ​ //輸出HelloWorld! 多行註釋:註釋一段文字,以/ * 開始以 * / 結束! ​ /* 這是我們Java程式的主入口, main方法也是程式的主線程。 */ 文檔註釋:用於生產A ...
  • JZ78 把二叉樹列印成多行 題目 給定一個節點數為 n 二叉樹,要求從上到下按層列印二叉樹的 val 值,同一層結點從左至右輸出,每一層輸出一行, 將輸出的結果存放到一個二維數組中返回。 例如:給定的二叉樹是{1,2,3,#,#,4,5} [ [1], [2,3], [4,5] ] 方法 非遞歸層 ...
  • 1. 協議的作用 TCP/IP 中消息傳輸基於流的方式,沒有邊界 協議的目的就是劃定消息的邊界,制定通信雙方要共同遵守的通信規則 2. Redis 協議 如果我們要向 Redis 伺服器發送一條 set name Nyima 的指令,需要遵守如下協議 // 該指令一共有3部分,每條指令之後都要添加回 ...
  • JVM是運行在操作系統之上的,它與硬體沒有直接的交互。先說一下JVM的記憶體區域,當函數開始運行時,JVM拿到自己的記憶體將自己的記憶體區域進行了分割,分為五塊區域:線程共用的有堆、方法區,線程私有的有java棧、本地方法棧、程式計數器。 ...
  • 公司直招 急聘崗位 ♦telegram:@xiaobai04 @HRdajisi♦釘釘:馬新宇 零九四五五五壹零壹六吧♦Skype: live:.cid.d850fdc83f05e44a♦郵箱:[email protected] ♦技術部:薪資面議 薪資範圍25-100k 績效:MA ...
  • 來源:blog.csdn.net/randompeople/article/details/114917087 為什麼 java wait/notify 必須與 synchronized 一起使用 這個問題就是書本上沒怎麼講解,就是告訴我們這樣處理,但沒有解釋為什麼這麼處理?我也是基於這樣的困惑去了 ...
  • 在2012 年 10 月,一個叫 Mike Youngstrom 的人在 Spring Jira 中創建了一個功能請求,要求在 Spring Framework 中支持無容器 Web 應用程式體繫結構,提出了在主容器引導 Spring 容器內配置 Web 容器服務;這件事情對 SpringBoot ... ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...