SpringCloudGateway 動的転送バックエンド サービス

API ゲートウェイの中核となる機能は、トラフィックのエントリを一元化し、ルーティングと転送を実現することです。Spring Cloud Gateway は、API ゲートウェイによって開発されたテクノロジーの 1 つです。また、OpenResty をベースにした Kong と ApiSix も人気があります。技術スタック。

Spring Cloud Gateway の設定ファイルを介してシンプルなルーティングと転送を実装できますが、ビジネス シナリオによっては、ルーティング設定のバックエンド サービス アドレスを動的に置き換える必要があるため、設定ファイルだけではこの要件を満たすことができません。

この記事では、ルーティング設定をデータベースに保存し、インターフェイス リクエストの特定の条件に従ってデータベースからバックエンド サービス アドレスを動的に読み取り、柔軟な転送を実現する方法を紹介します。

特定のコードについては、サンプル

I. 概要

Spring Cloud Gateway の関連するルーティング設定ルールをデータベースに保存することで、動的かつ柔軟にルーティングを調整できます。この記事の実装では、ヘッダー内の特定の値をリクエストすることで、対応するバックエンド サービス アドレスを動的に選択します。

2. 依存関係をプロジェクトに追加する

プロジェクトのgradleに依存関係を追加します。

ビルド.gradle:

plugins {
    
    
    id 'org.springframework.boot' version '3.0.2'
    id 'io.spring.dependency-management' version '1.1.0'
    id 'java'
}

group = 'cn.springcamp'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'

configurations {
    
    
    compileOnly {
    
    
        extendsFrom annotationProcessor
    }
    testCompileOnly {
    
    
        extendsFrom testAnnotationProcessor
    }
}

repositories {
    
    
    mavenCentral()
}

dependencies {
    
    
    implementation "org.springframework.boot:spring-boot-starter-json"
    implementation 'org.springframework.boot:spring-boot-starter-validation'
    implementation 'org.springframework.boot:spring-boot-starter-data-r2dbc'
    implementation 'org.springframework.cloud:spring-cloud-starter-gateway'
    runtimeOnly 'com.h2database:h2'
    runtimeOnly 'io.r2dbc:r2dbc-h2'
    annotationProcessor 'org.projectlombok:lombok'
    testAnnotationProcessor 'org.projectlombok:lombok'
    testImplementation "org.springframework.boot:spring-boot-starter-test"
    testImplementation 'org.junit.vintage:junit-vintage-engine'
    testImplementation 'io.projectreactor:reactor-test'
    testImplementation 'com.h2database:h2'
    testImplementation 'io.r2dbc:r2dbc-h2'
    testImplementation 'org.junit.vintage:junit-vintage-engine'
}

dependencyManagement {
    
    
    imports {
    
    
        mavenBom "org.springframework.cloud:spring-cloud-dependencies:2022.0.1"
    }
}

test {
    
    
    useJUnitPlatform()
}

Spring Cloud Gateway は Spring Web Flux テクノロジーに基づいて構築されているため、依存関係のデータベース構成には r2dbc を使用する必要があります。

3. 設定ファイル

サンプル プログラムでは、構成ファイル、構成ファイル コードを使用して基本的なルーティングを構成することを好みます。

spring:
  r2dbc:
    url: r2dbc:h2:mem:///testdb?options=DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
    username: sa
    password:
  cloud:
    gateway:
      routes:
        - id: routeOne
          predicates:
            - Path=/route1/**
          uri: no://op
          filters:
            - UriHostPlaceholderFilter=10001
        - id: routeTwo
          predicates:
            - Path=/route2/**
          uri: no://op
          filters:
            - UriHostPlaceholderFilter=10001

構成ファイルには 2 つのルートが構成されており、対応するインターフェイス アドレス パスはそれぞれ/route1/**Path=/route2/**。パス内の はあいまい一致を***意味し、/route1/パスの先頭に が付いている限り、アクセスできます。

バックエンド サービス アドレスは、意図しないアドレスで構成されています:uri: no://op処理ロジックがデータベースから構成を読み取ることでバックエンド サービス アドレスを動的に置き換えるためです。

3. ダイナミックルーティングデータの格納形式

ROUTE_FILTER_ENTITYこのデータベース テーブルを使用して、インターフェイス バックエンド サービス構成データを保存します。テーブル構造は次のとおりです。

CREATE TABLE "ROUTE_FILTER_ENTITY"
(
   id VARCHAR(255) PRIMARY KEY,
   route_id VARCHAR(255),  -- 路由ID,对应配置文件中的 ```id```配置项
   code VARCHAR(255), -- 接口请求header中的code参数的值
   url VARCHAR(255) -- 后端服务地址
);

クライアントが/route1/testインターフェイス、設定ファイルのルーティング設定に従って、Spring Cloud Gatewayid: routeOneはこのルーティング ルールにヒットします。このルールに対応するバックエンド サービス アドレスは、uri: no://op私たちが期待する実際のバックエンド サービス アドレスではありません。

したがって、実際のバックエンド サービス アドレスを読み取り、リクエストをこのアドレスに転送する必要があります。RouteId とインターフェイス リクエストのヘッダーのコード パラメーターの値に従って、対応するバックエンド サービス アドレスurlフィールド。

バックエンドサービスのアドレスを読み取ったので、このアドレスにリクエストを転送する必要があります。転送方法は以下のとおりです。

4. バックエンドサービスの動的転送

動的転送はフィルターをカスタマイズすることで実装されます。カスタム フィルターのコードは次のとおりです。

@Component
public class UriHostPlaceholderFilter extends AbstractGatewayFilterFactory<UriHostPlaceholderFilter.Config> {
    
    
    @Autowired
    private RouteFilterRepository routeFilterRepository;

    public UriHostPlaceholderFilter() {
    
    
        super(Config.class);
    }

    @Override
    public List<String> shortcutFieldOrder() {
    
    
        return Collections.singletonList("order");
    }

    @Override
    public GatewayFilter apply(Config config) {
    
    
        return new OrderedGatewayFilter((exchange, chain) -> {
    
    
            String code = exchange.getRequest().getHeaders().getOrDefault("code", new ArrayList<>()).stream().findFirst().orElse("");
            String routeId = exchange.getAttribute(GATEWAY_PREDICATE_MATCHED_PATH_ROUTE_ID_ATTR);
            if (StringUtils.hasText(code)) {
    
    
                String newurl;
                try {
    
    
                    newurl = routeFilterRepository.findByRouteIdAndCode(routeId, code).toFuture().get().getUrl();
                } catch (InterruptedException | ExecutionException e) {
    
    
                    throw new RuntimeException(e);
                }
                if (StringUtils.hasText(exchange.getRequest().getURI().getQuery())) {
    
    
                    newurl = newurl + "?" + exchange.getRequest().getURI().getQuery();
                }
                URI newUri = null;
                try {
    
    
                    newUri = new URI(newurl);
                } catch (URISyntaxException e) {
    
    
                    log.error("uri error", e);
                }

                exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, newUri);
            }
            return chain.filter(exchange);
        }, config.getOrder());
    }

    @Data
    @NoArgsConstructor
    public static class Config {
    
    
        private int order;

        public Config(int order) {
    
    
            this.order = order;
        }
    }
}

AbstractGatewayFilterFactory クラスを拡張することにより、UriHostPlaceholderFilter フィルターをカスタマイズしました。

コードの中核となるロジックは apply メソッドにあります。

まず、 を介してインターフェイス要求ヘッダーのパラメーター コードの値を取得String code = exchange.getRequest().getHeaders().getOrDefault("code", new ArrayList<>()).stream().findFirst().orElse("")できます。

次に、 を通じてrouteIdを取得String routeId = exchange.getAttribute(GATEWAY_PREDICATE_MATCHED_PATH_ROUTE_ID_ATTR)できます。

最後に、 を渡すことで、構成されたバックエンド サービス アドレスをデータベースから読み取るnewurl = routeFilterRepository.findByRouteIdAndCode(routeId, code).toFuture().get().getUrl()ことができます。

バックエンド サービス アドレスを取得した後、 を呼び出して、リクエストを対応するアドレスにexchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, newUri);転送します。

4.単体テスト

単体テスト コードでは、バックエンド サービスの動的構成データを事前設定します。

insert into ROUTE_FILTER_ENTITY values('1','routeOne','alpha','http://httpbin.org/anything')

次に、/route1/test?a=testこのインターフェイスへの。設定に従って、リクエストは に転送されますhttp://httpbin.org/anything

単体テストの実行後、インターフェイスから返されたデータがバックエンド サービス http://httpbin.org/anything から返されたデータであることがログからわかります。

バックエンド サービス アドレスを調整する場合は、ROUTE_FILTER_ENTITY テーブル内のこの構成データの URL フィールドを他のサービス アドレスに変更するだけで済み、プログラムの柔軟性が大幅に向上します。

おすすめ

転載: blog.csdn.net/haiyan_qi/article/details/129112234