Apache ShenYu 閘道器正式支援 Dubbo3 服務代理

語言: CN / TW / HK

作者:劉良

Apache Dubbo 在去年釋出了下一代的雲原生微服務版本 Dubbo3,目前最新版本 Dubbo3 已在阿里經濟體完成對 HSF2 框架的全面替換與升級,Dubbo3 目前已成為社群企業實踐推薦版本。Apache Shenyu 閘道器在這個背景下發布了對 Dubbo3 服務代理的支援。

本文介紹瞭如何通過 Apache ShenYu 閘道器訪問 Dubbo 服務,主要內容包括從簡單示例到核心呼叫流程分析,並對設計原理進行了總結。

1.png

介紹

Apache ShenYu

2.png

Apache ShenYu(Incubating)是一個非同步的,高效能的,跨語言的,響應式的 API 閘道器。相容各種主流框架體系,支援熱插拔,使用者可以定製化開發,滿足使用者各種場景的現狀和未來需求,經歷過大規模場景的錘鍊。

2021 年 5 月,ShenYu 捐獻給 Apache 軟體基金會,Apache 基金會全票通過,順利進入孵化器。

Apache Dubbo

Dubbo3 是下一代的雲原生微服務框架,全面升級了包括下一代 RPC 協議、應用級服務發現、Dubbo Mesh、統一服務治理等核心能力,多語言 Java、Golang 同步釋出 3.0 特性。目前最新版本 Dubbo3 已在阿里經濟體完成對 HSF2 框架的全面替換與升級,包括阿里核心電商、阿里雲、活餓了麼、釘釘、考拉等都已經全面升級 Dubbo3,2022 雙 11 大促核心系統將跑在 Dubbo3 之上,社群使用者包括工商銀行、小米、平安健康等也已成功升級 Dubbo3 核心功能。

Dubbo 快速開始

本小節介紹如何將 Dubbo 服務接入到 ShenYu 閘道器,您可以直接在工程下找到本小節的示例程式碼 。

啟動 shenyu-admin

shenyu-admin 是 Apache ShenYu 後臺管理系統, 啟動的方式有多種,本文通過本地部署的方式啟動。啟動成功後,需要在基礎配置->外掛管理中,把 dubbo 外掛設定為開啟,並設定你的註冊地址,請確保註冊中心已經開啟。

3.png

啟動 shenyu 閘道器

在這裡通過原始碼的方式啟動,直接執行 shenyu-bootstrap 中的 ShenyuBootstrapApplication。

在啟動前,請確保閘道器已經引入相關依賴。如果客戶端是 apache dubbo,註冊中心使用 zookeeper,請參考如下配置:

```

    <dependency>
        <groupId>org.apache.shenyu</groupId>
        <artifactId>shenyu-spring-boot-starter-plugin-apache-dubbo</artifactId>
        <version>${project.version}</version>
    </dependency>
    <dependency>
        <groupId>org.apache.dubbo</groupId>
        <artifactId>dubbo</artifactId>
        <version>3.0.8</version>
    </dependency>
    <!-- Dubbo zookeeper registry dependency start -->
    <dependency>
        <groupId>org.apache.curator</groupId>
        <artifactId>curator-client</artifactId>
        <version>4.0.1</version>
        <exclusions>
            <exclusion>
                <artifactId>log4j</artifactId>
                <groupId>log4j</groupId>
            </exclusion>
        </exclusions>
    </dependency>
    <dependency>
        <groupId>org.apache.curator</groupId>
        <artifactId>curator-framework</artifactId>
        <version>4.0.1</version>
    </dependency>
    <dependency>
        <groupId>org.apache.curator</groupId>
        <artifactId>curator-recipes</artifactId>
        <version>4.0.1</version>
    </dependency>
    <!-- Dubbo zookeeper registry dependency end -->
    <!-- apache dubbo plugin end-->

```

啟動 shenyu-examples-dubbo

以官網提供的例子為例 shenyu-examples-dubbo 。假如 dubbo 服務定義如下:

```

<dubbo:application name="test-dubbo-service"/>
<dubbo:registry address="${dubbo.registry.address}"/>
<dubbo:protocol name="dubbo" port="20888"/>

<dubbo:service timeout="10000" interface="org.apache.shenyu.examples.dubbo.api.service.DubboTestService" ref="dubboTestService"/>

```

宣告應用服務名稱,註冊中心地址,使用 dubbo 協議,宣告服務介面,對應介面實現類:

``` /* * DubboTestServiceImpl. / @Service("dubboTestService") public class DubboTestServiceImpl implements DubboTestService {

@Override
@ShenyuDubboClient(path = "/findById", desc = "Query by Id")
public DubboTest findById(final String id) {
    return new DubboTest(id, "hello world shenyu Apache, findById");
}

//......

} ```

在介面實現類中,使用註解@ShenyuDubboClient 向 shenyu-admin 註冊服務。

在配置檔案application.yml中的配置資訊:

``` server: port: 8011 address: 0.0.0.0 servlet: context-path: / spring: main: allow-bean-definition-overriding: true dubbo: registry: address: zookeeper://localhost:2181 # dubbo使用的註冊中心

shenyu: register: registerType: http #註冊方式 serverLists: http://localhost:9095 #註冊地址 props: username: admin password: 123456 client: dubbo: props: contextPath: /dubbo
appName: dubbo ```

在配置檔案中,宣告 dubbo 使用的註冊中心地址,dubbo 服務向 shenyu-admin 註冊,使用的方式是 http,註冊地址是 http://localhost:9095。關於註冊方式的使用,請參考應用客戶端接入。

呼叫 dubbo 服務

shenyu-examples-dubbo 專案成功啟動之後會自動把加 @ShenyuDubboClient 註解的介面方法註冊到閘道器。

開啟 外掛列表 -> Proxy -> dubbo 可以看到外掛規則配置列表:

4.png

註冊成功的選擇器資訊:

5.png

註冊成功的規則資訊:

6.png

選擇器和規則是 Apache ShenYu 閘道器中最靈魂的東西。掌握好它,你可以對任何流量進行管理。對應為選擇器與規則裡面的匹配條件(conditions),根據不同的流量篩選規則,我們可以處理各種複雜的場景。流量篩選可以從 Header, URI, Query, Cookie 等等 Http 請求獲取資料。

然後可以採用 Match,=,Regex,Groovy,Exclude 等匹配方式,匹配出你所預想的資料。多組匹配新增可以使用 And/Or 的匹配策略。具體的介紹與使用請看: 選擇器與規則管理 。

發起 GET 請求,通過 ShenYu 閘道器呼叫 dubbo 服務:

GET http://localhost:9195/dubbo/findById?id=100 Accept: application/json

成功響應之後,結果如下:

{ "name": "hello world shenyu Apache, findById", "id": "100" }

至此,就成功的通過 http 請求訪問 dubbo 服務了,ShenYu 閘道器通過 shenyu-plugin-dubbo 模組將 http 協議轉成了 dubbo 協議。

深入理解 Dubbo 外掛

在執行上述 demo 的過程中,是否存在一些疑問:

  • dubbo 服務是如何註冊到 shenyu-admin?
  • shenyu-admin 是如何將資料同步到 ShenYu 閘道器?
  • DubboPlugin 是如何將 http 協議轉換到到 dubbo 協議?

帶著這些疑問,來深入理解 dubbo 外掛。

應用客戶端接入

應用客戶端接入是指將微服務接入到 Apache ShenYu 閘道器,當前支援 Http、 Dubbo、 Spring Cloud、 gRPC、 Motan、 Sofa、 Tars 等協議的接入。

將應用客戶端接入到 Apache ShenYu 閘道器是通過註冊中心來實現的,涉及到客戶端註冊和服務端同步資料。註冊中心支援 Http、Zookeeper、Etcd、Consul 和 Nacos。預設是通過 Http 方式註冊。

客戶端接入的相關配置請參考客戶端接入配置。

  • 客戶端註冊

7.png

在你的微服務配置中宣告註冊中心客戶端型別,如 Http 或 Zookeeper。應用程式啟動時使用 SPI 方式載入並初始化對應註冊中心客戶端,通過實現 Spring Bean 相關的後置處理器介面,在其中獲取需要進行註冊的服務介面資訊,將獲取的資訊放入 Disruptor 中。

註冊中心客戶端從 Disruptor 中讀取資料,並將介面資訊註冊到 shenyu-admin,Disruptor 在其中起資料與操作解耦的作用,利於擴充套件。

  • 服務端註冊

8.png

在 shenyu-admin 配置中宣告註冊中心服務端型別,如 Http 或 Zookeeper。當 shenyu-admin 啟動時,讀取配置型別,載入並初始化對應的註冊中心服務端,註冊中心服務端收到 shenyu-client 註冊的介面資訊後,將其放入 Disruptor 中,然後會觸發註冊處理邏輯,將服務介面資訊更新併發布同步事件。

Disruptor 在其中起到資料與操作解耦,利於擴充套件。如果註冊請求過多,導致註冊異常,也有資料緩衝作用。

資料同步原理

資料同步是指在 shenyu-admin 後臺操作資料以後,使用何種策略將資料同步到 Apache ShenYu 閘道器。Apache ShenYu 閘道器當前支援ZooKeeper、WebSocket、Http長輪詢、Nacos 、Etcd 和 Consul 進行資料同步。預設是通過WebSocket進行資料同步。

資料同步的相關配置請參考資料同步配置。

  • 資料同步的意義

閘道器是流量請求的入口,在微服務架構中承擔了非常重要的角色,閘道器高可用的重要性不言而喻。在使用閘道器的過程中,為了滿足業務訴求,經常需要變更配置,比如流控規則、路由規則等等。因此,閘道器動態配置是保障閘道器高可用的重要因素。

當前資料同步特性如下:

1、所有的配置都快取在 Apache ShenYu 閘道器記憶體中,每次請求都使用本地快取,速度非常快。

2、使用者可以在 shenyu-admin 後臺任意修改資料,並馬上同步到閘道器記憶體。

3、支援 Apache ShenYu 的外掛、選擇器、規則資料、元資料、簽名資料等資料同步。

4、所有外掛的選擇器,規則都是動態配置,立即生效,不需要重啟服務。

5、資料同步方式支援 Zookeeper、Http 長輪詢、Websocket、Nacos、Etcd 和 Consul。

  • 資料同步原理分析

下圖展示了 Apache ShenYu 資料同步的流程,Apache ShenYu 閘道器在啟動時,會從配置服務同步配置資料,並且支援推拉模式獲取配置變更資訊,然後更新本地快取。管理員可以在管理後臺(shenyu-admin),變更使用者許可權、規則、外掛、流量配置,通過推拉模式將變更資訊同步給 Apache ShenYu 閘道器,具體是 push 模式,還是 pull 模式取決於使用哪種同步方式。

9.png

在最初的版本中,配置服務依賴 Zookeeper 實現,管理後臺將變更資訊 push 給閘道器。而現在可以支援 WebSocket、Http長輪詢、Zookeeper、Nacos、Etcd 和 Consul,通過在配置檔案中設定 shenyu.sync.${strategy} 指定對應的同步策略,預設使用 webosocket 同步策略,可以做到秒級資料同步。但是,有一點需要注意的是,Apache ShenYu閘道器 和 shenyu-admin 必須使用相同的同步策略。

如上圖所示,shenyu-admin 在使用者發生配置變更之後,會通過 EventPublisher 發出配置變更通知,由 EventDispatcher 處理該變更通知,然後根據配置的同步策略(http、weboscket、zookeeper、naocs、etcd、consul),將配置傳送給對應的事件處理器。

1、如果是 websocket 同步策略,則將變更後的資料主動推送給 shenyu-web,並且在閘道器層,會有對應的 WebsocketDataHandler 處理器來處理 shenyu-admin 的資料推送。

2、如果是 zookeeper 同步策略,將變更資料更新到 zookeeper,而 ZookeeperSyncCache 會監聽到 zookeeper 的資料變更,並予以處理。

3、如果是 http 同步策略,由閘道器主動發起長輪詢請求,預設有 90s 超時時間,如果 shenyu-admin 沒有資料變更,則會阻塞 http 請求,如果有資料發生變更則響應變更的資料資訊,如果超過 60s 仍然沒有資料變更則響應空資料,閘道器層接到響應後,繼續發起 http 請求,反覆同樣的請求。

流程分析

流程分析是從原始碼的角度,展示服務註冊流程,資料同步流程和服務呼叫流程。

  • 服務註冊流程

1、讀取 dubbo 服務

使用註解@ShenyuDubboClient 標記需要註冊到閘道器的 dubbo 服務。

註解掃描通過 ApacheDubboServiceBeanListener 完成,它實現了 ApplicationListener介面,在 Spring 容器啟動過程中,發生上下文重新整理事件時,開始執行事件處理方法 onApplicationEvent()。在重寫的方法邏輯中,讀取 Dubbo 服務 ServiceBean,構建元資料物件和 URI 物件,並向 shenyu-admin 註冊。具體的註冊邏輯由註冊中心實現,請參考客戶端接入原理。

2、處理註冊資訊

客戶端通過註冊中心註冊的元資料和 URI 資料,在 shenyu-admin 端進行處理,負責儲存到資料庫和同步給 shenyu 閘道器。Dubbo 外掛的客戶端註冊處理邏輯在 ShenyuClientRegisterDubboServiceImpl 中。繼承關係如下:

10.png

  • ShenyuClientRegisterService:客戶端註冊服務,頂層介面;
  • FallbackShenyuClientRegisterService:註冊失敗,提供重試操作;
  • AbstractShenyuClientRegisterServiceImpl:抽象類,實現部分公共註冊邏輯;
  • ShenyuClientRegisterDubboServiceImpl:實現 Dubbo 外掛的註冊;

註冊資訊包括選擇器,規則和元資料。

整體的 dubbo 服務註冊流程如下:

11.png

  • 資料同步流程

1、admin 更新資料

假設在在後臺管理系統中,新增一條選擇器資料,請求會進入 SelectorController 類中的 createSelector()方法,它負責資料的校驗,新增或更新資料,返回結果資訊。在 SelectorServiceImpl 類中通過 createOrUpdate()方法完成資料的轉換,儲存到資料庫,釋出事件,更新 upstream。

在 Service 類完成資料的持久化操作,即儲存資料到資料庫。釋出變更資料通過 eventPublisher.publishEvent()完成,這個 eventPublisher 物件是一個ApplicationEventPublisher 類,這個類的全限定名是 org.springframework.context.ApplicationEventPublisher,釋出資料的功能正是是通過 Spring 相關的功能來完成的。

當事件釋出完成後,會自動進入到 DataChangedEventDispatcher 類中的 onApplicationEvent()方法,根據不同資料型別和資料同步方式進行事件處理。

2、閘道器資料同步

閘道器在啟動時,根據指定的資料同步方式載入不同的配置類,初始化資料同步相關類。

在接收到資料後,進行反序列化操作,讀取資料型別和操作型別。不同的資料型別,有不同的資料處理方式,所以有不同的實現類。但是它們之間也有相同的處理邏輯,所以可以通過模板方法設計模式來實現。相同的邏輯放在抽象類 AbstractDataHandler 中的 handle()方法中,不同邏輯就交給各自的實現類。

新增一條選擇器資料,是新增操作,會進入到 SelectorDataHandler.doUpdate()具體的資料處理邏輯中。

在通用外掛資料訂閱者 CommonPluginDataSubscriber,負責處理所有外掛、選擇器和規則資訊。

將資料儲存到閘道器的記憶體中,BaseDataCache 是最終快取資料的類,通過單例模式實現。選擇器資料就存到了 SELECTOR_MAP 這個 Map 中。在後續使用的時候,也是從這裡拿資料。

上述邏輯用流程圖表示如下:

12.png

  • 服務呼叫流程

在 Dubbo 外掛體系中,類繼承關係如下:

13.png

ShenyuPlugin:頂層介面,定義介面方法; AbstractShenyuPlugin:抽象類,實現外掛共有邏輯; AbstractDubboPlugin:dubbo外掛抽象類,實現dubbo共有邏輯(ShenYu閘道器支援ApacheDubbo和AlibabaDubbo); ApacheDubboPlugin:ApacheDubbo外掛。

  • org.apache.shenyu.web.handler.ShenyuWebHandler.DefaultShenyuPluginChain#execute()

通過 ShenYu 閘道器代理後,請求入口是 ShenyuWebHandler,它實現了 org.springframework.web.server.WebHandler 介面,通過責任鏈設計模式將所有外掛連線起來。

  • org.apache.shenyu.plugin.base.AbstractShenyuPlugin#execute()

當請求到閘道器時,判斷某個外掛是否執行,是通過指定的匹配邏輯來完成。在 execute()方法中執行選擇器和規則的匹配邏輯。

  • org.apache.shenyu.plugin.global.GlobalPlugin#execute()

最先被執行的是 GlobalPlugin ,它是一個全域性外掛,在 execute()方法中構建上下文資訊。 

  • org.apache.shenyu.plugin.base.RpcParamTransformPlugin#execute()

接著被執行的是 RpcParamTransformPlugin , 它負責從 http 請求中讀取引數,儲存到 exchange 中,傳遞給 rpc 服務。在 execute()方法中,執行該外掛的核心邏輯:從 exchange 中獲取請求資訊,根據請求傳入的內容形式處理引數。

  • org.apache.shenyu.plugin.dubbo.common.AbstractDubboPlugin

然後被執行的是DubboPlugin 。在 doExecute()方法中,主要是檢查元資料和引數。在 doDubboInvoker()方法中設定特殊的上下文資訊,然後開始dubbo的泛化呼叫。

在 genericInvoker()方法中:

1、獲取 ReferenceConfig 物件;

2、獲取泛化服務 GenericService 物件;

3、構造請求引數 pair 物件;

4、發起非同步的泛化呼叫。

通過泛化呼叫就可以實現在閘道器呼叫 dubbo 服務了。

ReferenceConfig 物件是支援泛化呼叫的關鍵物件 ,它的初始化操作是在資料同步的時候完成的。

  • org.apache.shenyu.plugin.response.ResponsePlugin#execute()

最後被執行的是 ResponsePlugin ,它統一處理閘道器的響應結果資訊。處理型別由 MessageWriter 決定,類繼承關係如下:

14.png

MessageWriter:介面,定義訊息處理方法; NettyClientMessageWriter:處理 Netty 呼叫結果; RPCMessageWriter:處理 RPC 呼叫結果; WebClientMessageWriter:處理 WebClient 呼叫結果;

Dubbo 服務呼叫,處理結果是 RPCMessageWriter。

  • org.apache.shenyu.plugin.response.strategy.RPCMessageWriter#writeWith()

在 writeWith()方法中處理響應結果,獲取結果或處理異常。

分析至此,關於 Dubbo 外掛的原始碼分析就完成了,分析流程圖如下:

15.png

小結

本文從實際案例出發,由淺入深分析了 ShenYu 閘道器對 Dubbo 服務的代理過程。涉及到的主要知識點如下:

  • 通過責任鏈設計模式執行外掛;
  • 使用模板方法設計模式實現 AbstractShenyuPlugin,處理通用的操作型別;
  • 使用單例設計模式實現快取資料類 BaseDataCache;
  • 通過 springboot starter 即可引入不同的註冊中心和數同步方式,擴充套件性很好;
  • 通過 admin 支援規則熱更新,方便流量管控;
  • Disruptor 佇列是為了資料與操作解耦,以及資料緩衝。

有任何疑問,歡迎通過以下渠道聯絡社群:

  • Apache Shenyu 社群:

https://github.com/apache/incubator-shenyu

  • Dubbo3 社群:

https://github.com/apache/dubbo  https://github.com/apache/dubbo

企業使用者可搜尋釘釘群 34129986

作者簡介

劉良:Apache ShenYu PPMC