Spring Web Reactive | 2. WebClient | 2.1. 構成
WebClientを作成する最も簡単な方法は、静的ファクトリメソッドのいずれかを使用することです。
WebClient.create()WebClient.create(String baseUrl)
追加オプションとともにWebClient.builder()を使用できます。
uriBuilderFactory: ベースURLとして使用するためにカスタマイズしたUriBuilderFactory。defaultUriVariables: URIテンプレートを展開するときに使用するデフォルト値。defaultHeader: すべてのリクエストで使用されるヘッダー。defaultCookie: すべてのリクエストで使用されるCookie。defaultRequest:Consumerで各リクエストをカスタマイズします。filter: すべてのリクエストのクライアントフィルター。exchangeStrategies: HTTPメッセージreader/writerのカスタマイズ。clientConnector: HTTPクライアントライブラリの設定。
たとえば、次のようになります。
Java
WebClient client = WebClient.builder()
.codecs(configurer -> ... )
.build();
Kotlin
val webClient = WebClient.builder()
.codecs { configurer -> ... }
.build()
いったん構築されると、WebClientは不変です。ただし、次のように複製して変更されたコピーを作成できます。
Java
WebClient client1 = WebClient.builder()
.filter(filterA).filter(filterB).build();
WebClient client2 = client1.mutate()
.filter(filterC).filter(filterD).build();
// client1 has filterA, filterB
// client2 has filterA, filterB, filterC, filterD
Kotlin
val client1 = WebClient.builder()
.filter(filterA).filter(filterB).build()
val client2 = client1.mutate()
.filter(filterC).filter(filterD).build()
// client1 has filterA, filterB
// client2 has filterA, filterB, filterC, filterD
2.1.1. MaxInMemorySize
コーデックには、アプリケーションのメモリ問題に対応するため、メモリにデータをバッファリングする制限があります。デフォルトでは256KBに設定されています。これが不足すると、次のようなエラーが発生します。
org.springframework.core.io.buffer.DataBufferLimitException : Exceeded limit on max bytes to buffer
デフォルトのコーデック制限を変更するには、次のように設定します。
Java
WebClient webClient = WebClient.builder()
.codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(2 * 1024 * 1024))
.build();
Kotlin
val webClient = WebClient.builder()
.codecs { configurer -> configurer.defaultCodecs().maxInMemorySize(2 * 1024 * 1024) }
.build()
2.1.2. Reactor Netty
Reactor Nettyの設定をカスタマイズするには、事前構成済みのHttpClientを提供します。
Java
HttpClient httpClient = HttpClient.create().secure(sslSpec -> ...);
WebClient webClient = WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(httpClient))
.build();
Kotlin
val httpClient = HttpClient.create().secure { ... }
val webClient = WebClient.builder()
.clientConnector(ReactorClientHttpConnector(httpClient))
.build()
リソース(Resources)
デフォルトでは、HttpClientはイベントループスレッドとコネクションプールを含むreactor.netty.http.HttpResourcesのグローバルReactor Nettyリソースを使用します。これは、イベントループの同時実行には固定された共有リソースが望ましいため、推奨されるモードです。このモードでは、プロセスが終了するまでグローバルリソースはアクティブなままです。
サーバープロセスとタイミングが合っている場合、通常は明示的に終了する必要はありません。ただし、サーバーがプロセス内で開始および停止できる場合、たとえばWARとしてデプロイされたSpring MVCアプリケーションでは、globalResources=true(デフォルト)でReactorResourceFactory型のSpring管理Beanを宣言し、Reactor Nettyがグローバルであることを確認できます。次の例に示すように、Spring ApplicationContextが閉じられるとリソースは終了します。
Java
@Bean
public ReactorResourceFactory reactorResourceFactory() {
return new ReactorResourceFactory();
}
Kotlin
@Bean
fun reactorResourceFactory() = ReactorResourceFactory()
グローバルReactor Nettyリソースを使用しないことも選択できます。ただし、このモードでは、次の例のように、すべてのReactor Nettyクライアントおよびサーバーインスタンスが共有リソースを使用することを保証する責任があります。
Java
@Bean
public ReactorResourceFactory resourceFactory() {
ReactorResourceFactory factory = new ReactorResourceFactory();
factory.setUseGlobalResources(false); // (1)
return factory;
}
@Bean
public WebClient webClient() {
Function<HttpClient, HttpClient> mapper = client -> {
// Further customizations...
};
ClientHttpConnector connector =
new ReactorClientHttpConnector(resourceFactory(), mapper); // (2)
return WebClient.builder().clientConnector(connector).build(); // (3)
}
Kotlin
@Bean
fun resourceFactory() = ReactorResourceFactory().apply {
isUseGlobalResources = false // (1)
}
@Bean
fun webClient(): WebClient {
val mapper: (HttpClient) -> HttpClient = {
// Further customizations...
}
val connector = ReactorClientHttpConnector(resourceFactory(), mapper) // (2)
return WebClient.builder().clientConnector(connector).build() // (3)
}
- (1) グローバルリソースから独立したリソースを作成します。
- (2) リソースファクトリで
ReactorClientHttpConnectorコンストラクターを使用します。 - (3) コネクターを
WebClient.Builderへ接続します。
タイムアウト(Timeouts)
接続タイムアウトを構成するには:
Java
import io.netty.channel.ChannelOption;
HttpClient httpClient = HttpClient.create()
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000);
WebClient webClient = WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(httpClient))
.build();
Kotlin
import io.netty.channel.ChannelOption
val httpClient = HttpClient.create()
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000);
val webClient = WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(httpClient))
.build();
読み取りまたは書き込みタイムアウトを構成するには:
Java
import io.netty.handler.timeout.ReadTimeoutHandler;
import io.netty.handler.timeout.WriteTimeoutHandler;
HttpClient httpClient = HttpClient.create()
.doOnConnected(conn -> conn
.addHandlerLast(new ReadTimeoutHandler(10))
.addHandlerLast(new WriteTimeoutHandler(10)));
// Create WebClient...
Kotlin
import io.netty.handler.timeout.ReadTimeoutHandler
import io.netty.handler.timeout.WriteTimeoutHandler
val httpClient = HttpClient.create()
.doOnConnected { conn -> conn
.addHandlerLast(new ReadTimeoutHandler(10))
.addHandlerLast(new WriteTimeoutHandler(10))
}
// Create WebClient...
すべてのリクエストのレスポンスタイムアウトを設定するには:
Java
HttpClient httpClient = HttpClient.create()
.responseTimeout(Duration.ofSeconds(2));
// Create WebClient...
Kotlin
val httpClient = HttpClient.create()
.responseTimeout(Duration.ofSeconds(2));
// Create WebClient...
特定のリクエストのレスポンスタイムアウトを設定するには:
Java
WebClient.create().get()
.uri("https://example.org/path")
.httpRequest(httpRequest -> {
HttpClientRequest reactorRequest = httpRequest.getNativeRequest();
reactorRequest.responseTimeout(Duration.ofSeconds(2));
})
.retrieve()
.bodyToMono(String.class);
Kotlin
WebClient.create().get()
.uri("https://example.org/path")
.httpRequest { httpRequest: ClientHttpRequest ->
val reactorRequest = httpRequest.getNativeRequest<HttpClientRequest>()
reactorRequest.responseTimeout(Duration.ofSeconds(2))
}
.retrieve()
.bodyToMono(String::class.java)
2.1.3. Jetty
次の例は、Jetty HttpClient設定をカスタマイズする方法を示しています。
Java
HttpClient httpClient = new HttpClient();
httpClient.setCookieStore(...);
WebClient webClient = WebClient.builder()
.clientConnector(new JettyClientHttpConnector(httpClient))
.build();
Kotlin
val httpClient = HttpClient()
httpClient.cookieStore = ...
val webClient = WebClient.builder()
.clientConnector(new JettyClientHttpConnector(httpClient))
.build();
デフォルトでは、HttpClientは独自のリソース(Executor、Scheduler、ByteBufferPool)を作成し、プロセスが終了するかstop()が呼び出されるまでアクティブなままです。
次の例のように、JettyResourceFactoryをSpring管理Beanとして宣言することで、Jettyクライアントおよびサーバーの複数インスタンス間でリソースを共有し、Spring ApplicationContextが閉じられたときにリソースを確実に終了できます。
Java
@Bean
public JettyResourceFactory resourceFactory() {
return new JettyResourceFactory();
}
@Bean
public WebClient webClient() {
HttpClient httpClient = new HttpClient();
// Further customizations...
ClientHttpConnector connector =
new JettyClientHttpConnector(httpClient, resourceFactory()); (1)
return WebClient.builder().clientConnector(connector).build(); (2)
}
Kotlin
@Bean
fun resourceFactory() = JettyResourceFactory()
@Bean
fun webClient(): WebClient {
val httpClient = HttpClient()
// Further customizations...
val connector = JettyClientHttpConnector(httpClient, resourceFactory()) (1)
return WebClient.builder().clientConnector(connector).build() (2)
}
- (1) リソースファクトリで
JettyClientHttpConnectorコンストラクターを使用します。 - (2) コネクターを
WebClient.Builderへ接続します。
2.1.4. HttpComponents
次の例は、Apache HttpComponents HttpClient設定をカスタマイズする方法を示しています。
Java
HttpAsyncClientBuilder clientBuilder = HttpAsyncClients.custom();
clientBuilder.setDefaultRequestConfig(...);
CloseableHttpAsyncClient client = clientBuilder.build();
ClientHttpConnector connector = new HttpComponentsClientHttpConnector(client);
WebClient webClient = WebClient.builder().clientConnector(connector).build();
Kotlin
val client = HttpAsyncClients.custom().apply {
setDefaultRequestConfig(...)
}.build()
val connector = HttpComponentsClientHttpConnector(client)
val webClient = WebClient.builder().clientConnector(connector).build()