マイクロサービスゲートウェイの選定:Zuul から Spring Cloud Gateway へ
マイクロサービスアーキテクチャにおいて、API ゲートウェイは重要なコンポーネントです。かつて Netflix Zuul が標準として広く採用されていましたが、現在は Spring Cloud Gateway への移行が進んでいます。この両者にはどのような違いがあり、なぜ移行が推奨されるのでしょうか。
1. Zuul と Spring Cloud Gateway の比較
1.1 背景とアーキテクチャ
Zuul は Netflix によって開発されたゲートウェイで、Spring Cloud 早期バージョンにおいて統合されていました。一方、Spring Cloud Gateway は Spring チームによってゼロから開発されたプロジェクトであり、Spring Cloud エコシステムの一部です。
Zuul 1.x はブロッキング I/O モデルを採用しており、パフォーマンスに課題がありました。Zuul 2.x では非阻塞 I/O へ移行しましたが、リリースの遅れや安定性の問題もあり、Spring Cloud Gateway が公式の推奨ゲートウェイとして位置づけられました。
1.2 パフォーマンス特性
一般的に Zuul 1.x は阻塞型、Spring Cloud Gateway は非阻塞型(Reactor パターン)と言われます。公式のベンチマークプロジェクト「spring-cloud-gateway-bench」による比較テストでは、以下のような結果が報告されています。
| コンポーネント | RPS (Requests Per Second) |
|---|---|
| Spring Cloud Gateway | 32213.38 |
| Zuul 1.x | 20800.13 |
| Linkerd | 28050.76 |
この結果から、Spring Cloud Gateway は Zuul 1.x と比較して約 1.6 倍のスループットを発揮することが確認できます。
2. Spring Cloud Gateway の概要
Spring Cloud Gateway は、Spring 5.0、Spring Boot 2.0、および Project Reactor を基盤として構築されています。マイクロサービスにおける API 路由管理を統一し、フィルタチェーンを通じてセキュリティ、モニタリング、レート制限などの機能を提供します。
2.1 主要な特徴
- Spring Framework 5 および Spring Boot 2.0 基于
- 動的な路由設定
- Predicate と Filter による細かな制御
- Hystrix による回路遮断器の統合
- Spring Cloud DiscoveryClient との連携
- カスタム Predicate および Filter の作成が容易
- レート制限およびパス書き換え機能
2.2 主要用語
- Route(路由): ゲートウェイの基本構成要素。ID、URI、Predicate の集合、Filter の集合で定義されます。
- Predicate(断言): Java 8 の Predicate 関数。ServerWebExchange を入力とし、HTTP リクエストの属性(ヘッダー、パラメータなど)に基づいて真偽を判定します。
- Filter(过滤器): GatewayFilter のインスタンス。リクエストまたはレスポンスを加工するために使用されます。
2.3 処理フロー
クライアントからのリクエストは、Gateway Handler Mapping によって一致する Route を検索されます。一致した場合、Gateway Web Handler に送られ、設定された Filter チェーンを経由してバックエンドサービスへ転送されます。Filter は「pre」(プロキシ前)と「post」(プロキシ後)のフェーズで実行可能です。
3. 実装ガイド
Spring Cloud Gateway の設定には、YAML ファイルによる宣言的な方法と、Java コードによる Bean 定義の方法の 2 通りがあります。維持性の観点から、YAML 設定が推奨されます。
3.1 依存関係の定義
プロジェクトの POM ファイルには、WebFlux を含むゲートウェイstarter を追加します。従来の Web モジュールは不要です。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>cloud-native-platform</artifactId>
<groupId>com.example.microservices</groupId>
<version>1.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>edge-service</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
</dependencies>
</project>
3.2 YAML による路由設定
application.yml にて路由ルールを定義します。
server:
port: 8080
spring:
application:
name: edge-gateway
cloud:
gateway:
routes:
- id: external_site_route
uri: https://httpbin.org
predicates:
- Path=/proxy/**
filters:
- StripPrefix=1
この設定により、http://localhost:8080/proxy/get へのアクセスは https://httpbin.org/get へ転送されます。
3.3 Java コードによる設定
RouteLocator Bean を定義することで、プログラム的に路由を制御できます。
@SpringBootApplication
public class ApiGatewayApp {
@Bean
public RouteLocator defineRoutes(RouteLocatorBuilder builder) {
return builder.routes()
.route(r -> r.path("/data/**")
.filters(f -> f.stripPrefix(1))
.uri("https://mockapi.io"))
.build();
}
public static void main(String[] args) {
SpringApplication.run(ApiGatewayApp.class, args);
}
}
4. Route Predicate の活用
Spring Cloud Gateway は、多様な条件に基づいた路由マッチングをサポートしています。内置された Predicate Factory を組み合わせることで、柔軟な制御が可能です。
4.1 時間に基づくマッチング
特定の日時以降、以前、または期間内のリクエストのみを許可できます。
spring:
cloud:
gateway:
routes:
- id: time_limited_route
uri: http://backend-service.local
predicates:
- After=2023-01-01T00:00:00+09:00[Asia/Tokyo]
After は指定時刻以降、Before は以前、Between は期間内を意味します。タイムゾーンは Asia/Tokyo 等形式で指定可能です。
4.2 Cookie によるマッチング
特定の Cookie 名とその値(正規表現)が一致する場合に路由されます。
spring:
cloud:
gateway:
routes:
- id: auth_cookie_route
uri: http://backend-service.local
predicates:
- Cookie=session_id, valid_.*
テスト例:curl http://localhost:8080 --cookie "session_id=valid_123"
4.3 ヘッダー属性によるマッチング
HTTP ヘッダーの値を条件にできます。
spring:
cloud:
gateway:
routes:
- id: trace_header_route
uri: http://backend-service.local
predicates:
- Header=X-User-Type, admin
テスト例:curl http://localhost:8080 -H "X-User-Type: admin"
4.4 ホスト名によるマッチング
Host ヘッダーのドメイン名をパターンマッチングします。
spring:
cloud:
gateway:
routes:
- id: subdomain_route
uri: http://backend-service.local
predicates:
- Host=*.service.local
4.5 HTTP メソッドによるマッチング
GET、POST などの HTTP メソッドでフィルタリングします。
spring:
cloud:
gateway:
routes:
- id: write_operation_route
uri: http://backend-service.local
predicates:
- Method=POST,PUT
4.6 パスによるマッチング
URI パスをパターンで匹配します。
spring:
cloud:
gateway:
routes:
- id: versioned_api_route
uri: http://backend-service.local
predicates:
- Path=/api/{version}/**
例:/api/v1/users は匹配しますが、/api/users は匹配しません。
4.7 クエリパラメータによるマッチング
クエリパラメータの存在または値を条件にします。
spring:
cloud:
gateway:
routes:
- id: feature_flag_route
uri: http://backend-service.local
predicates:
- Query=enable_feature, true
テスト例:curl localhost:8080?enable_feature=true
4.8 IP アドレスによるマッチング
リモートアドレスの CIDR ブロックを指定します。
spring:
cloud:
gateway:
routes:
- id: internal_network_route
uri: http://backend-service.local
predicates:
- RemoteAddr=10.0.0.1/8
4.9 複数条件の組み合わせ
複数の Predicate を同時に設定できます。この場合、すべての条件を満たす必要があります。
spring:
cloud:
gateway:
routes:
- id: complex_rule_route
uri: http://backend-service.local
predicates:
- Host=api.service.local
- Path=/secure/**
- Method=GET
- Header=X-Auth-Token, \w+
- Query=region, jp
- After=2023-06-01T00:00:00+09:00[Asia/Tokyo]
複数の Route に匹配する可能性がある場合、最初に定義された Route が優先されて処理されます。