Spring 开发人员,您是否曾经觉得需要一个易于使用且高效的 API 异步/非阻塞的 HTTP 客户端?如果是,WebClient 是你的选择。WebClient 是 Spring5 中引入的新的被动 HTTP 客户端。
WebClient 是 Spring5 的响应性 Web 框架 Spring WebFlux 的一部分。要使用 WebClient,您需要将 spring-webflux 模块包含在您的项目中。
在项目 pom.xml 文件中添加如下依赖:
<!-- 引入 webflux 依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency>
请注意,您需要 Spring Boot 2.xx 版本才能使用 Spring WebFlux 模块。
Spring Webflux 是 Spring Framework 5.0 添加的新功能,WebFlux 本身追随当下最火的 Reactive Programming 而诞生的框架。
传统的 Web 框架,比如说:Struts2,Spring MVC 等都是基于 Servlet API 与 Servlet 容器基础之上运行的,在 Servlet 3.1 之后才有了异步非阻塞的支持。而 WebFlux 是一个典型非阻塞异步的框架,它的核心是基于 Reactor 的相关 API 实现的。相对于传统的 Web 框架来说,它可以运行在诸如 Netty,Undertow 及支持 Servlet 3.1 的容器上,因此它的运行环境的可选择性要比传统 Web 框架多的多。
根据官方的说法,Webflux 主要在如下两方面体现出独有的优势:
其实在 servlet 3.1 提供了非阻塞的 API,WebFlux 提供了一种比其更完美的解决方案。使用非阻塞的方式可以利用较小的线程或硬件资源来处理并发进而提高其可伸缩性
老生常谈的编程方式了,Spring5 必须让你使用 java8,那么函数式编程就是 java8 重要的特点之一,而 WebFlux 支持函数式编程来定义路由端点处理请求。
我们可以通过 ReactorLoadBalancerExchangeFilterFunction 实现 Spring WebFlux WebClient 负载均衡功能。使用方式如下:
WebClient.ResponseSpec responseSpec = WebClient.builder().baseUrl(url) .filter(lbFunction) // ReactorLoadBalancerExchangeFilterFunction 实例 .build().method(HttpMethod.GET) // POST 请求 .retrieve(); // 请求结果的方法
ReactorLoadBalancerExchangeFilterFunction 是一个 ExchangeFilterFunction,它使用 ReactiveLoadBalancer 对正确的 ServiceInstance 执行请求。其中,ExchangeFilterFunction 表示过滤交换函数的函数。当订阅服务器订阅由 WebClient 返回的发布服务器时,将执行该过滤器。
注意:下面示例假设你已经使用 Eureka Server 注册中心注册了一个名为 USER 的服务,分别运行在 7001、7002 和 7003 端口上面。关于怎样搭建 USER 服务请参考 “Netflix Ribbon 与 Spring Cloud 集成”。
本文示例将通过代码演示 ReactorLoadBalancerExchangeFilterFunction 去实现 WebClient 负载均衡调用。代码如下:
(1)application.yml 配置文件,指定 Eureka 注册中心地址:
server: port: 8080 spring: application: name: demo eureka: client: serviceUrl: # 指定注册中心 defaultZone: http://localhost:8077/eureka/
(2)实例代码,注入 ReactorLoadBalancerExchangeFilterFunction 实例,然后在 WebClient.builder() 中通过 filter() 方法去使用它。如下:
package com.hxstrive.springcloud.load_balancer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.client.loadbalancer.reactive.ReactorLoadBalancerExchangeFilterFunction; import org.springframework.http.HttpMethod; import org.springframework.stereotype.Component; import org.springframework.web.reactive.function.client.WebClient; import reactor.core.publisher.Mono; @Component public class Demo1 { @Autowired private ReactorLoadBalancerExchangeFilterFunction lbFunction; public void demo() { // 如果你要实现负载均衡,调用地址不能写具体主机地址 // 需要将主机地址替换成对于服务的服务名,即 spring.application.name 指定的值 // 你也可以到 Eureka server 中去查看 String url = "http://USER/info"; for(int i = 0; i < 21; i++) { WebClient.ResponseSpec responseSpec = WebClient.builder().baseUrl(url) .filter(lbFunction) .build().method(HttpMethod.GET) // post请求 .retrieve(); // 请求结果的方法 // 将结果转换为相应的类型,这是String,直接返回即可 Mono<String> mono = responseSpec.bodyToMono(String.class); String response = mono.block(); if(null == response) { System.out.println("ClientResponse is null"); continue; } System.out.println(response); } } }
(3)通过单元测试去调用 demo() 方法,如下:
import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ActiveProfiles; @SpringBootTest class Demo1Test { @Autowired private Demo1 demo; @Test void run() { demo.demo(); } }
输出结果如下:
From user1 - Mon Apr 19 12:58:39 CST 2021 From user3 - Mon Apr 19 12:58:40 CST 2021 From user2 - Mon Apr 19 12:58:41 CST 2021 ... From user1 - Mon Apr 19 12:58:50 CST 2021 From user3 - Mon Apr 19 12:58:50 CST 2021 From user2 - Mon Apr 19 12:58:50 CST 2021
Spring WebFlux WebClient 还提供了一个非响应性负载均衡客户端 LoadBalancerExchangeFilterFunction,如果 Spring Webflux 在类路径中,则会自动配置 LoadBalancerExchangeFilterFunction。下面示例显示如何配置 WebClient 及使用负载均衡器:
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.client.loadbalancer.reactive.LoadBalancerExchangeFilterFunction; import org.springframework.http.HttpMethod; import org.springframework.stereotype.Component; import org.springframework.web.reactive.function.client.WebClient; import reactor.core.publisher.Mono; @Component public class Demo1 { @Autowired private LoadBalancerExchangeFilterFunction lbFunction; public void demo() { // 如果你要实现负载均衡,调用地址不能写具体主机地址 // 需要将主机地址替换成对于服务的服务名,即 spring.application.name 指定的值 // 你也可以到 Eureka server 中去查看 String url = "http://USER/info"; for(int i = 0; i < 21; i++) { WebClient.ResponseSpec responseSpec = WebClient.builder().baseUrl(url) .filter(lbFunction) .build().method(HttpMethod.GET) // post请求 .retrieve(); // 请求结果的方法 // 将结果转换为相应的类型,这是String,直接返回即可 Mono<String> mono = responseSpec.bodyToMono(String.class); String response = mono.block(); if(null == response) { System.out.println("ClientResponse is null"); continue; } System.out.println(response); } } }