WebClient
是Spring5引入的响应式非阻塞Http客户端,同时支持阻塞式调用。其中exchange()
方法是非阻塞式的, 而retrieve()
方法是阻塞式的。WebClient内部将实际发出请求的操作委托给一个HTTP客户端库,默认是Reactor Netty。
下面首先写一个代码示例:
WebClient webClient = WebClient.create();
Mono<ClientResponse> response = webClient.get()
.uri("http://localhost:8080/resp")
.exchange();
ClientResponse clientResponse = response.block();
Mono<Resp> respMono = clientResponse.bodyToMono(Resp.class);
Resp rp = respMono.block();
System.out.println("rp = " + rp);
首先分析第一步创建WebClient客户端:
static WebClient create() {
return new DefaultWebClientBuilder().build();
}
跟踪进去可以发现他创建的类型是DefaultWebClient
类型。
然后是get调用:
@Override
public RequestHeadersUriSpec<?> get() {
return methodInternal(HttpMethod.GET);
}
private RequestBodyUriSpec methodInternal(HttpMethod httpMethod) {
return new DefaultRequestBodyUriSpec(httpMethod);
}
返回一个Request
的定义,可以指定request header, uri, body, cookie等。
然后是uri方法:
@Override
public RequestBodySpec uri(String uriTemplate, Object... uriVariables) {
attribute(URI_TEMPLATE_ATTRIBUTE, uriTemplate);
return uri(uriBuilderFactory.expand(uriTemplate, uriVariables));
}
通过uriBuilderFactory
来替换url中的占位符。
exchange方法:
@Override
public Mono<ClientResponse> exchange() {
ClientRequest request = (this.inserter != null ?
initRequestBuilder().body(this.inserter).build() :
initRequestBuilder().build());
return Mono.defer(() -> exchangeFunction.exchange(request)
.checkpoint("Request to " + this.httpMethod.name() + " " + this.uri + " [DefaultWebClient]")
.switchIfEmpty(NO_HTTP_CLIENT_RESPONSE_ERROR));
}
首先创建一个Request对象:
private ClientRequest.Builder initRequestBuilder() {
if (defaultRequest != null) {
defaultRequest.accept(this);
}
return ClientRequest.create(this.httpMethod, initUri())
.headers(headers -> headers.addAll(initHeaders()))
.cookies(cookies -> cookies.addAll(initCookies()))
.attributes(attributes -> attributes.putAll(this.attributes));
}
build:
@Override
public ClientRequest build() {
return new BodyInserterRequest(this.method, this.url, this.headers, this.cookies, this.body, this.attributes);
}
添加header, cookie以及attributes到request对象中,然后返回BodyInserterRequest
类型的Request对象。
然后看exchange方法返回的类型:
Mono.defer(() -> exchangeFunction.exchange(request)
.checkpoint("Request to " + this.httpMethod.name() + " " + this.uri + " [DefaultWebClient]")
.switchIfEmpty(NO_HTTP_CLIENT_RESPONSE_ERROR));
其实它通过Mono.defer返回了一个延迟调用的Mono对象, 然后具体在这里ClientResponse clientResponse = response.block();
会调用exchangeFunction.exchange(request)
。
ExchangeFunction
根据文档, 根据ClientRequest对象,获得延迟的ClientResponse对象,即执行实际的发出请求获取结果的操作。
而exchangeFunction
的默认类型是DefaultExchangeFunction
,这里看下它的exchange()方法:
@Override
public Mono<ClientResponse> exchange(ClientRequest clientRequest) {
Assert.notNull(clientRequest, "ClientRequest must not be null");
HttpMethod httpMethod = clientRequest.method();
URI url = clientRequest.url();
String logPrefix = clientRequest.logPrefix();
return this.connector
.connect(httpMethod, url, httpRequest -> clientRequest.writeTo(httpRequest, this.strategies))
.doOnRequest(n -> logRequest(clientRequest))
.doOnCancel(() -> logger.debug(logPrefix + "Cancel signal (to close connection)"))
.map(httpResponse -> {
logResponse(httpResponse, logPrefix);
return new DefaultClientResponse(
httpResponse, this.strategies, logPrefix, httpMethod.name() + " " + url,
() -> createRequest(clientRequest));
});
}
因为WebClient默认使用Reactor Netty库, 所以这里的connector类型默认是ReactorClientHttpConnector
。
connector的最基础的接口类型是ClientHttpConnector
, 根据文档是HTTP客户端的抽象,提供了必要的属性,方法这些,发送请求(ClientHttpRequest), 返回结果(ClientHttpResponse)。它和ExchangeFunction
的关系时,ExchangeFunction
持有ClientHttpConnector
属性。
上面一个方法实际的操作在connect
方法里, 下面的都是做一些log及转换,下面进入connect方法:
@Override
public Mono<ClientHttpResponse> connect(HttpMethod method, URI uri,
Function<? super ClientHttpRequest, Mono<Void>> requestCallback) {
if (!uri.isAbsolute()) {
return Mono.error(new IllegalArgumentException("URI is not absolute: " + uri));
}
return this.httpClient
.request(io.netty.handler.codec.http.HttpMethod.valueOf(method.name()))
.uri(uri.toString())
.send((request, outbound) -> requestCallback.apply(adaptRequest(method, uri, request, outbound)))
.responseConnection((res, con) -> Mono.just(adaptResponse(res, con.inbound(), con.outbound().alloc())))
.next();
}
如果不是绝对路径,抛出异常。
如果是绝对路径,继续往下,httpClient是HttpClient
类型,默认实现类是HttpClientHeaders
,经过request, uri和send方法分别填充请求类型, url, 以及TcpClient的body属性后,返回的是HttpClientFinalizer
类型。
然后通过调用HttpClientFinalizer
的responseConnection
方法,返回Flux<ClientHttpResponse)
类型。实际的请求操作其实是委托给了HttpClientFinalizer
类中的TcpClient
对象。
然后TcpClient返回一个MonoHttpConnect
对象, 它继承自Mono<Connection>
,因此,只有在block()
获取对象的时候才会真正执行。
@Override
public Mono<? extends Connection> connect(Bootstrap b) {
SslProvider ssl = SslProvider.findSslSupport(b);
if (b.config()
.group() == null) {
LoopResources loops = HttpResources.get();
EventLoopGroup elg = loops.onClient(LoopResources.DEFAULT_NATIVE);
b.group(elg)
.channel(loops.onChannel(elg));
}
HttpClientConfiguration conf = HttpClientConfiguration.getAndClean(b);
ClientCookieEncoder cookieEncoder = conf.cookieEncoder;
ClientCookieDecoder cookieDecoder = conf.cookieDecoder;
BootstrapHandlers.channelOperationFactory(b,
(ch, c, msg) -> new HttpClientOperations(ch, c, cookieEncoder, cookieDecoder));
if (ssl != null) {
if (ssl.getDefaultConfigurationType() == null) {
switch (conf.protocols) {
case HttpClientConfiguration.h11:
ssl = SslProvider.updateDefaultConfiguration(ssl, SslProvider.DefaultConfigurationType.TCP);
break;
case HttpClientConfiguration.h2:
ssl = SslProvider.updateDefaultConfiguration(ssl, SslProvider.DefaultConfigurationType.H2);
break;
}
}
SslProvider.setBootstrap(b, ssl);
}
SslProvider defaultSsl = ssl;
if (conf.deferredConf != null) {
return Mono.fromCallable(() -> new HttpClientConfiguration(conf))
.transform(conf.deferredConf)
.flatMap(c -> new MonoHttpConnect(b, c, defaultClient, defaultSsl));
}
return new MonoHttpConnect(b, conf, defaultClient, defaultSsl);
}
当然这些都是Reactor Netty中的内容,有兴趣的可以看看这个库。