Spring5 WebClient源码简析

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类型。

然后通过调用HttpClientFinalizerresponseConnection方法,返回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中的内容,有兴趣的可以看看这个库。

猜你喜欢

转载自blog.csdn.net/adolph09/article/details/106179613