실무에서 webflux와 webclient를 사용하는 API 게이트웨이에 작업하면서 내가 지금 뭐하고 있는건지 전혀 모르겠어서 스프링 공식문서를 정리한다. 다 정리하는게 목표는 아니고 일단 일하는데 필요한 만큼씩만 그때그때 정리한다!
스프링 웹 MVC는 서브렛 스택을 위해 만들어졌다. 당시에는 블록킹 API만 지원을 했었고, 이후 논블록킹 API를 지원하지만 기존 프레임워크들은 많은 부분 블록킹 API를 사용했다. 리액티브 스택의 웹 프레임워크인 스프링 웹플럭스는 나중에 5.0 버전에서 추가되었다. 웹플럭스는 완전히 논블락킹이고 리액티브 스트림 백프레셔를 지원하고 Netty, Undertow, Servlet 컨테이너와 같은 서버에서 동작한다. 둘 모두 스프링에서 옵셔널한 모듈로 어플리케이션은 둘 중 하나를 사용하거나 MVC에 WebClient처럼 둘 다 사용할 수도 있다
웹플럭스가 탄생한 이유는 작은 고정된 스레드 수로 동시성을 처리하고, 적은 하드웨어 리소스로 스케일하기 위해 논블락킹 웹 스택이 필요했기 때문이다. 또 다른 이유는 함수형 프로그래밍이다. 자바 8부터 람다식을 지원하면서, 비동기적 로직의 선언적 합성을 하기 좋아졌다.
Reactive Streams
defines interaction between async components with back pressureReactive Streams
is to let subscriber control rate of publisher producing data. Reactive Streams
establish mechanism and boundary for publisher and subscriberReactive Streams
is too low-level, higher level API is needed for application logic Reactor
is reactive lib used in webflux. it provides Mono
and Flux
API to work on data seq 0..1 and 0..NReactor
has vocab similar to ReactiveX
Reactor
support non-blocking back pressure Publisher
of Reactive Streams as input and returns Flux
or Mono
spring-web
module contains reactive foundation such as Reactive Streams
adapter for servers and core WebHandler
API (similar to Servelt API but non-blocking)WebClient
. Greater the latency or dependency among calls, the more dramatic the benefitstomcat
, jetty
, servlet container
, netty
, and undertow
spring-boot-starter-webflux
by default uses netty
javax.servlet.ServletRequest
or javax.servlet.Filter
spring web abstraction such as org.springframework.http.server.reactiveServerHttpRequest
or org.springframework.web.server.WebFilter
should be usedpublishOn
operator to process on diff thread. But it is not recommendedWebClient
operates in event loop style. there may be fixed number of thread for it. However, if netty is used for both client and server, two share event loop resources by defaultpublishOn
operator. shcedulers have names that suggest concurrency strategy. spring-web
module contains foundational support for reactive web app. For server request processing, there are two levels of support. 1) HttpHandler: Basic contract for HTTP req handling. 2) WebHandler API: higher-level, general-purpose web API for req handling, on top of which annotated controllers anf unctional enpoints are built. For client side, basic ClientHttpConnector
contract is there to performance HTTP req. WebClient
is built on top of this. For client and server, codecs
for serialization and deserialization of HTTP req and res content
public interface HttpHandler {
Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response);
}
org.springframework.web.server
package builds on the HttpHandler
contract to provide api for processing req thru chain of WebExceptionHandler
, WebFilter
, and WebHandler
HttpsHandler
simply abstracts the use of different HTTP servers, WebHandler
API aims to provide a broader set of eatures commonly used in web app such as user session, req attributes, locale etc.WebFilter
can be used to apply interception logic before and after the rest of the processing chain of filters and the target WebHandler WebExceptionHandler
can be used to handle exceptions from the chain of WebFilter
instances and the target WebHandler
spring-web
and spring-core
modules provide support for serializing and deserializing byte content to and from higher level objects through non-blocking I/O with Reactive Streams back pressure.ClientCodecConfigurer
andServerCodecConfigurer
are typically used to configure and customize the codecs to use in an applicationval webClient = WebClient.builder()
.codecs({ configurer ->
val decoder = CustomDecoder()
configurer.customCodecs().registerWithDefaultConfig(decoder)
})
.build()
WebClient
is client to perform HTTP request for Spring WebFluxWebClient
requires HTTP client lib to perform req. Reactor Netty, JDK HttpClient, etc. has built-in support ClientHttpConnector
WebClient
is to use create()
static factory methodWebClient.builder()
can be used to configure more options uriBuilderFactory
: Customized UriBuilderFactory to use as a base URL.defaultUriVariables
: default values to use when expanding URI templates.defaultHeader
: Headers for every request.defaultCookie
: Cookies for every request.defaultRequest
: Consumer to customize every request.filter
: Client filter for every request.exchangeStrategies
: HTTP message reader/writer customizations.clientConnector
: HTTP client library settings.val webClient = WebClient.builder()
.codecs { configurer -> configurer.defaultCodecs().maxInMemorySize(2 * 1024 * 1024) }
.build()
HttpClient
val httpClient = HttpClient.create().secure { ... }
val webClient = WebClient.builder()
.clientConnector(ReactorClientHttpConnector(httpClient))
.build()
HttpClient
participates in the global Reactor Netty resources held in reactor.netty.http.HttpResources
, including event loop threads and a connection pool.ReactorResourceFactory
bean to ensure netty global resources are shut down when ApplicationContext
is closedval httpClient = HttpClient.create()
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000);
val webClient = WebClient.builder()
.clientConnector(ReactorClientHttpConnector(httpClient))
.build();
val httpClient = HttpClient.create()
.doOnConnected { conn -> conn
.addHandlerLast(ReadTimeoutHandler(10))
.addHandlerLast(WriteTimeoutHandler(10))
}
// Create WebClient...
val httpClient = HttpClient.create()
.responseTimeout(Duration.ofSeconds(2));
// Create WebClient...
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)
val httpClient = HttpClient.newBuilder()
.followRedirects(Redirect.NORMAL)
.connectTimeout(Duration.ofSeconds(20))
.build()
val connector = JdkClientHttpConnector(httpClient, DefaultDataBufferFactory())
val webClient = WebClient.builder().clientConnector(connector).build()
retrieve()
method can be used to declare how to extract the response