@Bean
public RouteLocator myRoutes(RouteLocatorBuilder builder) {
return builder.routes().build();
}
Spring Cloud Gateway の構築
このガイドでは、Spring Cloud Gateway の使用方法を説明します。
構築するもの
Spring Cloud Gateway (英語) を使用してゲートウェイを構築します。
必要なもの
約 15 分
Eclipse STS や IntelliJ IDEA のような任意の IDE または VSCode のようなテキストエディター
Java 17+
本ガイドの完成までの流れ
ほとんどの Spring 入門ガイドと同様に、最初から始めて各ステップを完了するか、すでに慣れている場合は基本的なセットアップステップをバイパスできます。いずれにしても、最終的に動作するコードになります。
最初から始めるには、Spring Initializr から開始に進みます。
基本をスキップするには、次の手順を実行します。
このガイドを Eclipse で「Spring 入門コンテンツのインポート」するか、ソースリポジトリをダウンロードして解凍、または、Git (英語) を使用してクローンを作成します。
git clone https://github.com/spring-guides/gs-gateway.git
gs-gateway/initial
に cd単純なルートを作成するにジャンプしてください。
完了したときは、gs-gateway/complete
のコードに対して結果を確認できます。
Spring Initializr から開始
IDE を使用する場合はプロジェクト作成ウィザードを使用します。IDE を使用せずにコマンドラインなどで開発する場合は、この事前に初期化されたプロジェクトからプロジェクトを ZIP ファイルとしてダウンロードできます。このプロジェクトは、このチュートリアルの例に合うように構成されています。
プロジェクトを手動で初期化するには:
IDE のメニューまたはブラウザーから Spring Initializr を開きます。アプリケーションに必要なすべての依存関係を取り込み、ほとんどのセットアップを行います。
Gradle または Maven のいずれかと、使用する言語を選択します。このガイドは、Java を選択したことを前提としています。
依存関係をクリックし、リアクティブゲートウェイ、Resilience4J、契約スタブランナーを選択します。
生成をクリックします。
結果の ZIP ファイルをダウンロードします。これは、選択して構成された Web アプリケーションのアーカイブです。
Eclipse や IntelliJ のような IDE は新規プロジェクト作成ウィザードから Spring Initializr の機能が使用できるため、手動での ZIP ファイルのダウンロードやインポートは不要です。 |
プロジェクトを Github からフォークして、IDE または他のエディターで開くこともできます。 |
単純なルートを作成する
Spring Cloud Gateway は、ルートを使用してダウンストリームサービスへのリクエストを処理します。このガイドでは、すべてのリクエストを HTTPBin (英語) にルーティングします。ルートはさまざまな方法で構成できますが、このガイドでは、ゲートウェイが提供する JavaAPI を使用します。
開始するには、Application.java
に型 RouteLocator
の新しい Bean
を作成します。
src/main/java/gateway/Application.java
myRoutes
メソッドは、ルートの作成に使用できる RouteLocatorBuilder
を取り込みます。ルートの作成に加えて、RouteLocatorBuilder
を使用すると、ルートに述語とフィルターを追加して、特定の条件に基づいてハンドルをルーティングしたり、必要に応じてリクエスト / レスポンスを変更したりできます。
これで、/get
のゲートウェイにリクエストが送信されたときに、リクエストを https://httpbin.org/get (英語)
にルーティングするルートを作成できます。このルートの構成では、World
の値を持つ Hello
リクエストヘッダーを、ルーティングされる前にリクエストに追加するフィルターを追加します。
src/main/java/gateway/Application.java
@Bean
public RouteLocator myRoutes(RouteLocatorBuilder builder) {
return builder.routes()
.route(p -> p
.path("/get")
.filters(f -> f.addRequestHeader("Hello", "World"))
.uri("http://httpbin.org:80"))
.build();
}
単純なゲートウェイをテストするために、ポート 8080
で Application.java
を実行できます。アプリケーションが実行されたら、http://localhost:8080/get
にリクエストを送信します。これを行うには、ターミナルで次の cURL コマンドを使用します。
$ curl http://localhost:8080/get
次の出力のようなレスポンスが返されます。
{
"args": {},
"headers": {
"Accept": "*/*",
"Connection": "close",
"Forwarded": "proto=http;host=\"localhost:8080\";for=\"0:0:0:0:0:0:0:1:56207\"",
"Hello": "World",
"Host": "httpbin.org",
"User-Agent": "curl/7.54.0",
"X-Forwarded-Host": "localhost:8080"
},
"origin": "0:0:0:0:0:0:0:1, 73.68.251.70",
"url": "http://localhost:8080/get"
}
HTTPBin は、World
の値を持つ Hello
ヘッダーがリクエストで送信されたことを示していることに注意してください。
Spring Cloud の使用 CircuitBreaker
これで、もう少し面白いことができます。ゲートウェイの背後にあるサービスは、動作が悪く、クライアントに影響を与える可能性があるため、作成したルートをサーキットブレーカーでラップすることをお勧めします。Resilience4J Spring Cloud CircuitBreaker 実装を使用して、Spring Cloud Gateway でこれを行うことができます。これは、リクエストに追加できる単純なフィルターを介して実装されます。これを実証するために別のルートを作成できます。
次の例では、HTTPBin の delay API を使用します。これは、レスポンスを送信する前に特定の秒数待機します。この API はレスポンスの送信に時間がかかる可能性があるため、この API を使用するルートをサーキットブレーカーでラップできます。次のリストは、RouteLocator
オブジェクトに新しいルートを追加します。
src/main/java/gateway/Application.java
@Bean
public RouteLocator myRoutes(RouteLocatorBuilder builder) {
return builder.routes()
.route(p -> p
.path("/get")
.filters(f -> f.addRequestHeader("Hello", "World"))
.uri("http://httpbin.org:80"))
.route(p -> p
.host("*.circuitbreaker.com")
.filters(f -> f.circuitBreaker(config -> config.setName("mycmd")))
.uri("http://httpbin.org:80")).
build();
}
この新しいルート構成と以前に作成したルート構成には、いくつかの違いがあります。1 つは、パス述語の代わりにホスト述語を使用することです。これは、ホストが circuitbreaker.com
である限り、リクエストを HTTPBin にルーティングし、そのリクエストをサーキットブレーカーでラップすることを意味します。これを行うには、ルートにフィルターを適用します。構成オブジェクトを使用して、サーキットブレーカーフィルターを構成できます。この例では、サーキットブレーカーに mycmd
という名前を付けます。
これで、この新しいルートをテストできます。そのためには、アプリケーションを起動する必要がありますが、今回は /delay/3
にリクエストを送信します。circuitbreaker.com
のホストを持つ Host
ヘッダーを含めることも重要です。それ以外の場合、リクエストはルーティングされません。次の cURL コマンドを使用できます。
$ curl --dump-header - --header 'Host: www.circuitbreaker.com' http://localhost:8080/delay/3
--dump-header を使用してレスポンスヘッダーを確認します。--dump-header の後の - は、ヘッダーを stdout に出力するように cURL に指示します。 |
このコマンドを使用すると、ターミナルに次のように表示されます。
HTTP/1.1 504 Gateway Timeout
content-length: 0
ご覧のとおり、HTTPBin からのレスポンスを待っている間にサーキットブレーカーがタイムアウトしました。サーキットブレーカーがタイムアウトした場合、オプションでフォールバックを提供して、クライアントが 504
ではなく、より意味のあるものを受信できるようにすることができます。たとえば、本番シナリオでは、キャッシュから一部のデータを返す場合がありますが、単純な例では、代わりに本文 fallback
を使用してレスポンスを返します。
これを行うには、サーキットブレーカーフィルターを変更して、タイムアウトの場合に呼び出す URL を提供します。
src/main/java/gateway/Application.java
@Bean
public RouteLocator myRoutes(RouteLocatorBuilder builder) {
return builder.routes()
.route(p -> p
.path("/get")
.filters(f -> f.addRequestHeader("Hello", "World"))
.uri("http://httpbin.org:80"))
.route(p -> p
.host("*.circuitbreaker.com")
.filters(f -> f.circuitBreaker(config -> config
.setName("mycmd")
.setFallbackUri("forward:/fallback")))
.uri("http://httpbin.org:80"))
.build();
}
これで、サーキットブレーカーでラップされたルートがタイムアウトすると、ゲートウェイアプリケーションで /fallback
が呼び出されます。これで、/fallback
エンドポイントをアプリケーションに追加できます。
Application.java
では、@RestController
クラスレベルのアノテーションを追加してから、次の @RequestMapping
をクラスに追加します。
src/main/java/gateway/Application.java
@RequestMapping("/fallback")
public Mono<String> fallback() {
return Mono.just("fallback");
}
この新しいフォールバック機能をテストするには、アプリケーションを再起動し、次の cURL コマンドを再度発行します
$ curl --dump-header - --header 'Host: www.circuitbreaker.com' http://localhost:8080/delay/3
フォールバックを設定すると、fallback
のレスポンス本文と共に 200
がゲートウェイから返されることがわかります。
HTTP/1.1 200 OK
transfer-encoding: chunked
Content-Type: text/plain;charset=UTF-8
fallback
テストの作成
優れた開発者として、ゲートウェイが期待どおりに動作していることを確認するためのテストを作成する必要があります。ほとんどの場合、特に単体テストでは、外部リソースへの依存関係を制限したいため、HTTPBin に依存しないでください。この問題の 1 つの解決策は、ルートの URI を構成可能にすることです。これにより、必要に応じて URI を変更できます。
そのために、Application.java
で、UriConfiguration
という新しいクラスを作成できます。
@ConfigurationProperties
class UriConfiguration {
private String httpbin = "http://httpbin.org:80";
public String getHttpbin() {
return httpbin;
}
public void setHttpbin(String httpbin) {
this.httpbin = httpbin;
}
}
ConfigurationProperties
を有効にするには、クラスレベルのアノテーションも Application.java
に追加する必要があります。
@EnableConfigurationProperties(UriConfiguration.class)
新しい構成クラスを配置すると、myRoutes
メソッドで使用できます。
src/main/java/gateway/Application.java
@Bean
public RouteLocator myRoutes(RouteLocatorBuilder builder, UriConfiguration uriConfiguration) {
String httpUri = uriConfiguration.getHttpbin();
return builder.routes()
.route(p -> p
.path("/get")
.filters(f -> f.addRequestHeader("Hello", "World"))
.uri(httpUri))
.route(p -> p
.host("*.circuitbreaker.com")
.filters(f -> f
.circuitBreaker(config -> config
.setName("mycmd")
.setFallbackUri("forward:/fallback")))
.uri(httpUri))
.build();
}
URL を HTTPBin にハードコードする代わりに、新しい構成クラスから URL を取得します。
次のリストは、Application.java
の完全な内容を示しています。
src/main/java/gateway/Application.java
@SpringBootApplication
@EnableConfigurationProperties(UriConfiguration.class)
@RestController
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
public RouteLocator myRoutes(RouteLocatorBuilder builder, UriConfiguration uriConfiguration) {
String httpUri = uriConfiguration.getHttpbin();
return builder.routes()
.route(p -> p
.path("/get")
.filters(f -> f.addRequestHeader("Hello", "World"))
.uri(httpUri))
.route(p -> p
.host("*.circuitbreaker.com")
.filters(f -> f
.circuitBreaker(config -> config
.setName("mycmd")
.setFallbackUri("forward:/fallback")))
.uri(httpUri))
.build();
}
@RequestMapping("/fallback")
public Mono<String> fallback() {
return Mono.just("fallback");
}
}
@ConfigurationProperties
class UriConfiguration {
private String httpbin = "http://httpbin.org:80";
public String getHttpbin() {
return httpbin;
}
public void setHttpbin(String httpbin) {
this.httpbin = httpbin;
}
}
これで、src/test/java/gateway
に ApplicationTest
という新しいクラスを作成できます。新しいクラスでは、次のコンテンツを追加します。
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
properties = {"httpbin=http://localhost:${wiremock.server.port}"})
@AutoConfigureWireMock(port = 0)
public class ApplicationTest {
@Autowired
private WebTestClient webClient;
@Test
public void contextLoads() throws Exception {
//Stubs
stubFor(get(urlEqualTo("/get"))
.willReturn(aResponse()
.withBody("{\"headers\":{\"Hello\":\"World\"}}")
.withHeader("Content-Type", "application/json")));
stubFor(get(urlEqualTo("/delay/3"))
.willReturn(aResponse()
.withBody("no fallback")
.withFixedDelay(3000)));
webClient
.get().uri("/get")
.exchange()
.expectStatus().isOk()
.expectBody()
.jsonPath("$.headers.Hello").isEqualTo("World");
webClient
.get().uri("/delay/3")
.header("Host", "www.circuitbreaker.com")
.exchange()
.expectStatus().isOk()
.expectBody()
.consumeWith(
response -> assertThat(response.getResponseBody()).isEqualTo("fallback".getBytes()));
}
}
テストでは、Spring Cloud Contract の WireMock を利用して、HTTPBin の API をモックできるサーバーを立ち上げます。最初に気付くのは、@AutoConfigureWireMock(port = 0)
の使用です。このアノテーションは、ランダムポートで WireMock を開始します。
次に、UriConfiguration
クラスを利用して、@SpringBootTest
アノテーションの httpbin
プロパティをローカルで実行されている WireMock サーバーに設定していることに注意してください。次に、テスト内で、ゲートウェイを介して呼び出す HTTPBin API の「スタブ」を設定し、期待される動作を模倣します。最後に、WebTestClient
を使用してゲートウェイにリクエストを行い、レスポンスを検証します。
要約
おめでとう! これで、最初の Spring Cloud Gateway アプリケーションが作成されました。
新しいガイドを作成したり、既存のガイドに貢献したいですか? 投稿ガイドラインを参照してください [GitHub] (英語) 。
すべてのガイドは、コード用の ASLv2 ライセンス、およびドキュメント用の帰属、NoDerivatives クリエイティブコモンズライセンス (英語) でリリースされています。 |