1. 序章

前回の記事では、 Spring Cloudゲートウェイとは何か、および組み込みの述語を使用して基本的なルーティングルールを実装する方法について説明しました。 ただし、これらの組み込み述語では不十分な場合があります。 たとえば、ルーティングロジックでは、何らかの理由でデータベースルックアップが必要になる場合があります。

このような場合、 Spring Cloud Gatewayを使用すると、 カスタム述語を定義できます。定義すると、他の述語として使用できます。つまり、流暢なAPIやFluentAPIを使用してルートを定義できます。 DSL。

2. 述語の構造

簡単に言うと、SpringCloudGatewayのPredicateは、指定されたリクエストが指定された条件を満たすかどうかをテストするオブジェクトです。 ルートごとに、フィルターを適用した後に構成されたバックエンドの要求を受け入れる1つ以上の述語を定義できます。

述語を書く前に、既存の述語のソースコード、より正確には、既存の PredicateFactoryのコードを見てみましょう。名前がすでに示唆しているように、Spring Cloudゲートウェイは人気のあるファクトリメソッドパターンは、拡張可能な方法で述語インスタンスの作成をサポートするメカニズムとして使用されます。

spring-cloud-gateway-coreorg.springframework.cloud.gateway.handler.predicateパッケージで利用可能な組み込みの述語ファクトリのいずれかを選択できます。 ]モジュール。 名前はすべてRoutePredicateFactoryで終わるため、既存のものを簡単に見つけることができます。 HeaderRouterPredicateFactoryは良い例です。

public class HeaderRoutePredicateFactory extends 
  AbstractRoutePredicateFactory<HeaderRoutePredicateFactory.Config> {

    // ... setup code omitted
    @Override
    public Predicate<ServerWebExchange> apply(Config config) {
        return new GatewayPredicate() {
            @Override
            public boolean test(ServerWebExchange exchange) {
                // ... predicate logic omitted
            }
        };
    }

    @Validated
    public static class Config {
        public Config(boolean isGolden, String customerIdCookie ) {
          // ... constructor details omitted
        }
        // ...getters/setters omitted
    }
}

実装で観察できる重要なポイントがいくつかあります。

  • それは拡張します AbstractRoutePredicateFactory 、順番に、を実装します RoutePredicateFactory ゲートウェイが使用するインターフェース
  • apply メソッドは、実際の Predicate – a GatewayPredicate のインスタンスを返します。この場合、
  • 述語は、テストロジックで使用される静的構成パラメーターを格納するために使用される内部Configクラスを定義します。

他の利用可能なPredicateFactory、を見ると、基本的なパターンは基本的に同じであることがわかります。

  1. 構成パラメーターを保持するConfigクラスを定義します
  2. テンプレートパラメーターとして構成クラスを使用して、AbstractRoutePredicateFactoryを拡張します
  3. apply メソッドをオーバーライドして、目的のテストロジックを実装するPredicateを返します。

3. カスタム述語ファクトリの実装

この実装では、次のシナリオを想定します。特定のAPIに対して、2つの可能なバックエンドから選択する必要がある呼び出し。 私たちの最も価値のある顧客である「ゴールデン」の顧客は、より多くのメモリ、より多くのCPU、および高速ディスクにアクセスできる強力なサーバーにルーティングする必要があります。 ゴールデンでない顧客は、それほど強力ではないサーバーにアクセスするため、応答時間が遅くなります。

リクエストがゴールデンカスタマーからのものであるかどうかを判断するには、リクエストに関連付けられた customerId を受け取り、そのステータスを返すサービスを呼び出す必要があります。 customerId については、単純なシナリオでは、Cookieで使用可能であると想定しています。

これらすべての情報を使用して、カスタム述語を記述できます。 既存の命名規則を維持し、クラスにGoldenCustomerRoutePredicateFactoryという名前を付けます。

public class GoldenCustomerRoutePredicateFactory extends 
  AbstractRoutePredicateFactory<GoldenCustomerRoutePredicateFactory.Config> {

    private final GoldenCustomerService goldenCustomerService;
    
    // ... constructor omitted

    @Override
    public Predicate<ServerWebExchange> apply(Config config) {        
        return (ServerWebExchange t) -> {
            List<HttpCookie> cookies = t.getRequest()
              .getCookies()
              .get(config.getCustomerIdCookie());
              
            boolean isGolden; 
            if ( cookies == null || cookies.isEmpty()) {
                isGolden = false;
            } else {                
                String customerId = cookies.get(0).getValue();                
                isGolden = goldenCustomerService.isGoldenCustomer(customerId);
            }              
            return config.isGolden() ? isGolden : !isGolden;           
        };        
    }
    
    @Validated
    public static class Config {        
        boolean isGolden = true;        
        @NotEmpty
        String customerIdCookie = "customerId";
        // ...constructors and mutators omitted   
    }    
}

ご覧のとおり、実装は非常に簡単です。 apply メソッドは、渡されたServerWebExchangeを使用して必要なロジックを実装するラムダを返します。 まず、 customerIdCookieの存在を確認します。 それが見つからない場合、これは通常の顧客です。 それ以外の場合は、Cookie値を使用してisGoldenCustomerサービスメソッドを呼び出します。

次に、クライアントのタイプを構成済みの isGolden パラメーターと組み合わせて、戻り値を決定します。これにより、isGoldenの値を変更するだけで、同じ述語を使用して前述の両方のルートを作成できます。パラメータ

4. カスタム述語ファクトリの登録

カスタム述語ファクトリをコーディングしたら、Spring CloudGatewayにifを認識させる方法が必要です。 Springを使用しているため、これは通常の方法で行われます。タイプ GoldenCustomerRoutePredicateFactoryのbeanを宣言します。

このタイプはRoutePredicateFactory throughが基本クラスであるため、コンテキストの初期化時にSpringによって選択され、SpringCloudGatewayで使用できるようになります。

ここでは、@Configurationクラスを使用してbeanを作成します。

@Configuration
public class CustomPredicatesConfig {
    @Bean
    public GoldenCustomerRoutePredicateFactory goldenCustomer(
      GoldenCustomerService goldenCustomerService) {
        return new GoldenCustomerRoutePredicateFactory(goldenCustomerService);
    }
}

ここでは、Springのコンテキストで利用可能な適切なGoldenCustomerService実装があると想定しています。 この例では、 customerId 値を固定値と比較するダミーの実装があります。現実的ではありませんが、デモンストレーションの目的には役立ちます。

5. カスタム述語の使用

「GoldenCustomer」述語が実装され、Spring Cloud Gatewayで利用できるようになったので、ルートの定義に使用を開始できます。 まず、流暢なAPIを使用してルートを定義し、次にYAMLを使用して宣言型の方法で定義します。

5.1. FluentAPIを使用したルートの定義

Fluent API は、プログラムで複雑なオブジェクトを作成する必要がある場合によく使用される設計上の選択肢です。 この例では、RouteLocatorBuilderとカスタム述語ファクトリを使用してRouteLocatorオブジェクトを作成する@Beanでルートを定義します。

@Bean
public RouteLocator routes(RouteLocatorBuilder builder, GoldenCustomerRoutePredicateFactory gf ) {
    return builder.routes()
      .route("golden_route", r -> r.path("/api/**")
        .uri("https://fastserver")
        .predicate(gf.apply(new Config(true, "customerId"))))
      .route("common_route", r -> r.path("/api/**")
        .uri("https://slowserver")
        .predicate(gf.apply(new Config(false, "customerId"))))                
      .build();
}

各ルートで2つの異なるConfig構成を使用したことに注目してください。 最初のケースでは、最初の引数は true であるため、ゴールデンカスタマーからの要求がある場合、述語もtrueと評価されます。 2番目のルートについては、コンストラクターで false を渡すため、述語はゴールデン以外の顧客に対してtrueを返します。

5.2. YAMLでルートを定義する

プロパティまたはYAMLファイルを使用して、宣言的な方法で以前と同じ結果を達成できます。 ここでは、少し読みやすいYAMLを使用します。

spring:
  cloud:
    gateway:
      routes:
      - id: golden_route
        uri: https://fastserver
        predicates:
        - Path=/api/**
        - GoldenCustomer=true
      - id: common_route
        uri: https://slowserver
        predicates:
        - Path=/api/**
        - name: GoldenCustomer
          args:
            golden: false
            customerIdCookie: customerId

ここでは、述語を定義するために使用可能な2つのオプションを使用して、以前と同じルートを定義しました。 最初のgolden_routeは、 Predicate = [param [、param]+]の形式をとるコンパクトな表現を使用します。  Predicate は、 RoutePredicateFactory サフィックスを削除することにより、ファクトリクラス名から自動的に派生する述語の名前です。 「=」記号の後に、関連するConfigインスタンスを設定するために使用されるパラメーターがあります。

このコンパクトな構文は、述語に単純な値だけが必要な場合は問題ありませんが、常にそうであるとは限りません。 これらのシナリオでは、2番目のルートに示されている長い形式を使用できます。 この場合、nameargsの2つのプロパティを持つオブジェクトを提供します。 name には述語名が含まれ、argsConfigインスタンスにデータを入力するために使用されます。 今回はargsがオブジェクトであるため、必要に応じて構成を複雑にすることができます。

6. テスト

次に、 curl を使用してゲートウェイをテストし、すべてが期待どおりに機能しているかどうかを確認しましょう。 これらのテストでは、前に示したようにルートを設定しましたが、ダミーのバックエンドとして公開されているhttpbin.orgサービスを使用します。 これは、ルールが期待どおりに機能しているかどうかをすばやく確認するために使用できる非常に便利なサービスであり、オンラインとローカルで使用できるDockerイメージの両方で利用できます。

テスト構成には、標準のAddRequestHeaderフィルターも含まれています。 これを使用して、述語の結果に対応する値を持つカスタムGoldencustomerヘッダーをリクエストに追加します。 また、バックエンドを呼び出す前にリクエストURIから/ api を削除するため、StripPrefixフィルターを追加します。

まず、「共通クライアント」シナリオをテストしてみましょう。 ゲートウェイが稼働している状態で、curlを使用してhttpbinheaders APIを呼び出します。これにより、受信したすべてのヘッダーがエコーされます。

$ curl http://localhost:8080/api/headers
{
  "headers": {
    "Accept": "*/*",
    "Forwarded": "proto=http;host=\"localhost:8080\";for=\"127.0.0.1:51547\"",
    "Goldencustomer": "false",
    "Host": "httpbin.org",
    "User-Agent": "curl/7.55.1",
    "X-Forwarded-Host": "localhost:8080",
    "X-Forwarded-Prefix": "/api"
  }
}

予想どおり、Goldencustomerヘッダーがfalse値で送信されたことがわかります。 「ゴールデン」のお客様と一緒に試してみましょう。

$ curl -b customerId=baeldung http://localhost:8080/api/headers
{
  "headers": {
    "Accept": "*/*",
    "Cookie": "customerId=baeldung",
    "Forwarded": "proto=http;host=\"localhost:8080\";for=\"127.0.0.1:51651\"",
    "Goldencustomer": "true",
    "Host": "httpbin.org",
    "User-Agent": "curl/7.55.1",
    "X-Forwarded-Host": "localhost:8080",
    "X-Forwarded-Prefix": "/api"
  }
}

今回は、Goldencustomertrueです。これは、ダミーサービスがゴールデンカスタマーに対して有効であると認識する値を持つcustomerIdCookieを送信したためです。

7. 結論

この記事では、カスタム述語ファクトリをSpring Cloud Gatewayに追加し、それらを使用して任意のロジックを使用してルートを定義する方法について説明しました。

いつものように、すべてのコードはGitHubを介して利用できます。