💡

Java | Spring Boot | API Gateway

2024/12/30に公開

Java Spring Boot

Routing

Spring Bootアプリケーションでは、HTTPリクエストをルーティングするために、@RestControllerや@Controllerアノテーションを使用してコントローラを定義する。これにより、異なるエンドポイントへのルーティングが可能となる。

@RestController
public class MyRouter {

    @GetMapping("/service1")
    public String handleService1() {
        return "Service 1 Response";
    }

    @GetMapping("/service2")
    public String handleService2() {
        return "Service 2 Response";
    }
}

Mavenで@RestController や @Controller アノテーションを使用するためには、pom.xml に以下の依存関係を追加する。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

Gateway

Spring Cloud Gatewayを使用することで、APIゲートウェイを簡単に構築できる。
Spring Cloud Gatewayは、ルーティング、フィルタリング、リクエスト/レスポンスの変換などを行う。

  • pom.xml に以下の依存関係を追加する。
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
  • application.yml または application.properties にGateway設定を追加する。

(application.ymlで設定する場合)

spring:
  cloud:
    gateway:
      routes:
        - id: service1
          uri: http://localhost:8081
          predicates:
            - Path=/service1
        - id: service2
          uri: http://localhost:8082
          predicates:
            - Path=/service2

(application.propertiesで設定する場合)

# application.yml での設定を application.properties 形式に変換するには、
# YAMLの階層構造をドット表記で表現する。
# routes[0] と routes[1] はそれぞれのルートのインデックスを示している。
# predicates[0] は、各ルートの条件を示している。

spring.cloud.gateway.routes[0].id=service1
spring.cloud.gateway.routes[0].uri=http://localhost:8081
spring.cloud.gateway.routes[0].predicates[0]=Path=/service1

spring.cloud.gateway.routes[1].id=service2
spring.cloud.gateway.routes[1].uri=http://localhost:8082
spring.cloud.gateway.routes[1].predicates[0]=Path=/service2

/service1 へのリクエストは http://localhost:8081 に、
/service2 へのリクエストは http://localhost:8082 にルーティングされるようになる。

リクエストフィルタリング

リクエストフィルタリングは、クライアントからのリクエストがバックエンドサービスに到達する前に行われる処理。これにより、リクエストの内容を検査し、必要に応じて変更や制限を加えることができる。

目的 内容
認証と認可 リクエストが正当なものであるかどうかを確認する。例えば、APIキーやJWTトークンの検証を行うなど。
リクエストの変換 リクエストパラメータやヘッダーの値を変更する。たとえば、特定のヘッダーを追加したり、値を変換したりする。
レート制限 クライアントのリクエスト数を制限し、サービスの過負荷を防ぐ。
  • application.yml または application.properties にGateway設定を追加する。

(application.ymlで設定する場合)

spring:
  cloud:
    gateway:
      routes:
        - id: my_route
          uri: http://localhost:8081
          predicates:
            - Path=/service
          filters:
            - AddRequestHeader=X-Request-Foo, Bar
            - RewritePath=/service, /

(application.propertiesで設定する場合)

spring.cloud.gateway.routes[0].id=my_route
spring.cloud.gateway.routes[0].uri=http://localhost:8081
spring.cloud.gateway.routes[0].predicates[0]=Path=/service
spring.cloud.gateway.routes[0].filters[0]=AddRequestHeader=X-Request-Foo, Bar
spring.cloud.gateway.routes[0].filters[1]=RewritePath=/service, /

/service へのリクエストが http://localhost:8081 に転送される際に、AddRequestHeaderフィルタによりX-Request-Foo ヘッダーが追加され、RewritePatフィルタによりパスが /service から / にリライトするリライトされる。

レスポンスフィルタリング

レスポンスフィルタリングは、バックエンドサービスからのレスポンスがクライアントに返される前に行われる処理。これにより、レスポンスの内容を変更したり、特定のデータを除外したりすることができる。

目的 内容
データの整形 レスポンスデータの形式を変更することが可能。たとえば、JSONデータのフィールドを追加したり、削除したりできる。
エラーハンドリング エラーレスポンスをカスタマイズし、クライアントに返す内容を明確にすることができる。
統計情報の追加 レスポンスに処理時間やトラフィック情報などのメタデータを追加できる。

Spring Cloud Gatewayでは、AddResponseHeaderフィルタを使用してレスポンスヘッダーを追加することができる。

(application.ymlで設定する場合)

spring:
  cloud:
    gateway:
      routes:
        - id: my_route
          uri: http://localhost:8081
          predicates:
            - Path=/service
          filters:
            - AddResponseHeader=X-Response-Foo, Bar

(application.propertiesで設定する場合)

spring.cloud.gateway.routes[0].id=my_route
spring.cloud.gateway.routes[0].uri=http://localhost:8081
spring.cloud.gateway.routes[0].predicates[0]=Path=/service
spring.cloud.gateway.routes[0].filters[0]=AddResponseHeader=X-Response-Foo, Bar

AddResponseHeaderフィルタによって、バックエンドのhttp://localhost:8081からのレスポンスにX-Response-Foo: Barヘッダーが追加される。

レスポンス内容の変換

レスポンス内容を変更するために、ModifyResponseBodyフィルタを使用することが可能。
このフィルタを使用すると、レスポンスのボディを特定の条件に基づいて変更することができる。

  • 以下は、JSONレスポンスの特定のフィールドを変更する例。

(application.ymlで設定する場合)

spring:
  cloud:
    gateway:
      routes:
        - id: my_route
          uri: http://localhost:8081
          predicates:
            - Path=/service
          filters:
            - ModifyResponseBody=Object

(application.propertiesで設定する場合)

spring.cloud.gateway.routes[0].id=my_route
spring.cloud.gateway.routes[0].uri=http://localhost:8081
spring.cloud.gateway.routes[0].predicates[0]=Path=/service
spring.cloud.gateway.routes[0].filters[0]=ModifyResponseBody=Object

ModifyResponseBodyフィルタを使用して、レスポンスボディをオブジェクトとして扱い、変更を加えることが可能。具体的な変換ロジックは、Javaでカスタムフィルタを作成することで実装できる。

import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@Component
public class CustomResponseFilter extends AbstractGatewayFilterFactory<CustomResponseFilter.Config> {

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

    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            // レスポンスをフィルタリング
            return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                // レスポンスのボディを変更するロジック
                exchange.getResponse().getHeaders().add("X-Custom-Header", "CustomValue");
            }));
        };
    }

    public static class Config {
        // 任意の設定
    }
}

このカスタムフィルタは、レスポンスヘッダーにX-Custom-Headerを追加する。
このカスタムフィルタを使用するために、Spring Cloud Gatewayのルート設定でこのカスタムフィルタを指定する。

(application.ymlで設定する場合)

spring:
  cloud:
    gateway:
      routes:
        - id: my_route
          uri: http://localhost:8081
          predicates:
            - Path=/service
          filters:
            - name: CustomResponseFilter

(application.propertiesで設定する場合)

spring.cloud.gateway.routes[0].id=my_route
spring.cloud.gateway.routes[0].uri=http://localhost:8081
spring.cloud.gateway.routes[0].predicates[0]=Path=/service
spring.cloud.gateway.routes[0].filters[0].name=CustomResponseFilter

リクエストのパスとレスポンスのステータスコードをコンソールに表示するカスタムフィルタ

import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;

@Component
public class CustomFilter extends AbstractGatewayFilterFactory<CustomFilter.Config> {

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

    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            // リクエスト処理
            System.out.println("Request Path: " + exchange.getRequest().getURI().getPath());
            return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                // レスポンス処理
                System.out.println("Response Status Code: " + exchange.getResponse().getStatusCode());
            }));
        };
    }

    public static class Config {
        // 任意の設定
    }
}

このカスタムフィルタは、リクエストのパスとレスポンスのステータスコードをコンソールに表示する。

LoadBalancer

Spring Cloud LoadBalancerを使用することで、サービス間の負荷分散を実現できる。
これにより、複数のインスタンスにリクエストを分散させることが可能となる。

  • pom.xml に以下の依存関係を追加する。
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
  • RestTemplateを使用して、サービスを呼び出す際にLoadBalancerを利用できるようになる。
@RestController
public class MyLoadBalancedService {

    @Autowired
    private RestTemplateBuilder restTemplateBuilder;

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return restTemplateBuilder.build();
    }

    @GetMapping("/callService")
    public String callService() {
        String response = restTemplate().getForObject("http://my-service/service-endpoint", String.class);
        return response;
    }
}

http://my-service/service-endpoint へのリクエストが複数のインスタンスに負荷分散される。

Discussion