环境:SpringBoot 2.6.12
1. 概述
Spring WebClient是一个基于Reactive编程模型的HTTP客户端,它允许您在Spring应用程序中以非阻塞的方式发送HTTP请求。WebClient是Spring 5中引入的一个重要功能,旨在提供一种更现代、高效和灵活的方式来与远程服务进行交互。
以下是关于Spring WebClient的一些描述信息:
非阻塞性:WebClient使用Reactor框架实现响应式编程,允许在发送HTTP请求时进行异步处理。这意味着您可以使用WebClient同时发送多个请求,并在它们都完成之前不阻塞应用程序的其他部分。
集成性:WebClient可以轻松地与Spring框架的其他组件集成,如Spring MVC、Spring WebFlux等。此外,它还支持各种HTTP消息转换器,使您可以轻松地将请求和响应数据转换为所需的Java对象。
自定义性:WebClient提供了丰富的自定义选项,例如超时设置、连接池配置、重试策略等。您可以通过配置WebClient的参数来满足特定的应用程序需求。
错误处理:WebClient支持优雅的错误处理机制。您可以使用异常处理程序捕获和处理HTTP请求中发生的错误,并根据需要采取适当的行动。
易于使用:WebClient提供了简单易用的API,使得发送HTTP请求变得非常简单。您可以使用Fluent API或Lambda表达式等方式来构建和发送HTTP请求,而无需编写大量的代码。
总之,Spring WebClient是一种功能强大的HTTP客户端,适用于基于响应式编程模型的现代应用程序。它提供了一种高效、可扩展和非阻塞的方式来与远程服务进行交互,并且可以轻松地集成到Spring应用程序中。
WebClient需要一个Http Client库来执行请求。内置支持以下功能:
Reactor Netty
https://github.com/reactor/reactor-netty
Jetty Reactive HttpClient
https://github.com/jetty-project/jetty-reactive-httpclient
Apache HttpComponents
https://hc.apache.org/index.html
其他可以通过ClientHttpConnector插入。
Spring WebClient与RestTemplate都是Spring框架提供的用于发送HTTP请求的工具,但它们之间存在一些关键差异。
2. WebClient & RestTemplate对比
阻塞与非阻塞:
RestTemplate使用Java Servlet API,这种模型在底层会为每个请求分配处理线程。这意味着线程会一直保持阻塞,直到Web客户端收到响应。这可能导致线程堆积,耗尽线程池并占掉所有可用内存。
WebClient是利用Spring Reactive框架提供的异步、非阻塞式解决方案。它为每个事件(HTTP调用)创建类似task的结构,而不是创建新线程。Reactive框架会在后台对这些task进行排队,只有在响应结果就绪后才开始执行。这允许应用程序在等待响应的同时不会阻塞正在执行的线程。
性能:
RestTemplate的阻塞式模型在处理大量并发请求时可能会导致性能下降。由于每个请求都需要一个独立的处理线程,因此线程上下文切换的开销可能会变得显著。
WebClient的非阻塞模型允许更有效地使用系统资源。由于它不阻塞线程,因此可以更高效地处理并发请求。此外,由于它使用事件驱动和异步逻辑,因此可以减少CPU和内存的使用。
适用场景:
RestTemplate长期以来一直是Spring的默认Web客户端,适用于大多数基于Spring的应用程序。它简单易用,并且已经为广大开发者所熟悉。
WebClient是Spring 5及之后版本引入的新特性,适用于构建响应式应用程序。它更适合处理大量并发请求,并且可以利用现代编程模型(如Reactive Streams API)的优势。
3. WebClient配置
创建WebClient最简单的方法是通过静态工厂方法之一:
WebClient#create()
WebClient#create(String baseUrl)
你也可以使用WebClient#builder提供更多的选项:
uriBuilderFactory: 定制的uriBuilderFactory用作基础URL。
defaultUriVariables: 在展开URI模板时使用的默认值。
defaultHeader: 每个请求的头文件。
defaultCookie: 每个请求的cookie。
defaultRequest: 自定义每个请求的使用者。
filter: 针对每个请求的客户端过滤器。
exchangeStrategies: HTTP消息读取器/写入器自定义。
clientConnector: HTTP客户端库设置。
4. 简单使用示例
示例1:
WebClient client = WebClient.builder().codecs(configurer -> ... ).build();
一旦创建了WebClient就是不可变的。不过,你可以克隆它并建立一个修改后的副本如下:
示例2:
WebClient client1 = WebClient.builder().filter(filterA).filter(filterB).build();// 创建一个副本WebClient client2 = client1.mutate().filter(filterC).filter(filterD).build();
5. 最大内存配置
为了避免应用程序内存问题,编解码器对内存中的数据缓存有限制。默认情况下,它们被设置为256KB。如果这还不够,你会得到以下错误:
org.springframework.core.io.buffer.DataBufferLimitException: Exceeded limit on max bytes to buffer
修改默认的最大内存
WebClient webClient = WebClient.builder().codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(2 * 1024 * 1024)).build();
6. Reactor Netty
网络请求Http Client。
自定义Reactor Netty设置,超时设置
HttpClient httpClient = HttpClient.create().option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000).doOnConnected(con -> {con.addHandlerFirst(new ReadTimeoutHandler(2, TimeUnit.SECONDS)) ;con.addHandlerLast(new WriteTimeoutHandler(10));});ClientHttpConnector connector = new ReactorClientHttpConnector(httpClient) ;Builder builder = WebClient.builder().clientConnector(connector) ;// 响应超时配置HttpClient httpClient = HttpClient.create().responseTimeout(Duration.ofSeconds(2));
7. 获取响应数据
WebClient client = WebClient.create("https://example.org");Mono<ResponseEntity<Person>> result = client.get().uri("/persons/{id}", id).accept(MediaType.APPLICATION_JSON).retrieve().toEntity(Person.class);
仅仅获取响应数据
Mono<Person> result = client.get().uri("/persons/{id}", id).accept(MediaType.APPLICATION_JSON).retrieve().bodyToMono(Person.class);
默认情况下,4xx或5xx响应会导致
WebClientResponseException,包括特定HTTP状态码的子类。要自定义错误响应的处理,使用onStatus处理程序如下所示:
Mono<Person> result = client.get().uri("/persons/{id}", id).accept(MediaType.APPLICATION_JSON).retrieve().onStatus(HttpStatus::is4xxClientError, response -> ...).onStatus(HttpStatus::is5xxServerError, response -> ...).bodyToMono(Person.class);
8. Exchange操作
exchangeToMono()和exchangeToFlux()方法对于需要更多控制的更高级情况很有用,比如根据响应状态对响应进行不同的处理:
@GetMapping("/removeInvoke3")public Mono<R> remoteInvoke3() {return wc.get().uri("http://localhost:9000/users/get?id={id}", new Random().nextInt(1000000)).exchangeToMono(clientResponse -> {if (clientResponse.statusCode().equals(HttpStatus.OK)) {return clientResponse.bodyToMono(Users.class);} else {return clientResponse.createException().flatMap(Mono::error) ;}}).log().flatMap(user -> Mono.just(R.success(user))).retry(3) // 重试次数.onErrorResume(ex -> {return Mono.just(R.failure(ex.getMessage())) ;});}
9. 请求体
请求体可以从ReactiveAdapterRegistry处理的任何异步类型进行编码,比如Mono如下例所示:
Mono<Person> personMono = ... ;Mono<Void> result = client.post().uri("/persons/{id}", id).contentType(MediaType.APPLICATION_JSON).body(personMono, Person.class).retrieve().bodyToMono(Void.class);
如果你有实际值,你可以使用bodyValue方法,如下例所示:
Person person = ... ;Mono<Void> result = client.post().uri("/persons/{id}", id).contentType(MediaType.APPLICATION_JSON).bodyValue(person).retrieve().bodyToMono(Void.class);
10. Form Data
要发送表单数据,可以提供MultiValueMap作为主体。注意,内容被FormHttpMessageWriter自动设置为
application/x-www-form-urlencoded。下面的例子展示了如何使用MultiValueMap:
MultiValueMap<String, String> formData = ... ;Mono<Void> result = client.post().uri("/path", id).bodyValue(formData).retrieve().bodyToMono(Void.class);
便捷方法
import static org.springframework.web.reactive.function.BodyInserters.*;Mono<Void> result = client.post().uri("/path", id).body(fromFormData("k1", "v1").with("k2", "v2")).retrieve().bodyToMono(Void.class);
11. 过滤器
你可以通过WebClient注册客户端过滤器(ExchangeFilterFunction)。生成器,以便拦截和修改请求,如下例所示:
WebClient client = WebClient.builder().filter((request, next) -> {ClientRequest filtered = ClientRequest.from(request).header("foo", "bar").build();return next.exchange(filtered);}).build();
完毕!!!
求关注、求转发



