原创文章,转载请注明原文章地址,谢谢!
在之前的一文中介绍了WebClient的简单使用,Spring5之WebClient简单使用。其实WebClient在服务调用方面提供了很多功能,需要的话,在网上也可以找到很多相关文章,后期如果在实际工作中用到的话,再来详细记录分享下,今天想要聊聊WebClient对负载均衡的支持。现在的微服务系统几乎不可能在服务端只部署一个实例了,所以负载均衡是微服务架构下必不可少的部分。RestTemplate对负载均衡的支持,只需要在注入RestTemplate的方法上添加@LoadBalanced注解,就自带负载均衡功能。那么WebClient如何支持负载均衡呢?笔者在测试时候也遇到一些坑,所以有必要记录一下研究的过程。
笔者在测试的时候准备三个微服务项目,也可以创建一个项目,启动三个实例,端口作为区分,整个集群作为微服务的提供者,注册中心使用阿里的nacos,如果不熟悉nacos的同学,可以百度一下nacos的使用,后期也会更新cloud alibaba相关的文章。提供者里面随便写一些简单的代码即可,笔者的测试代码如下
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>2.1.0.RELEASE</version>
</dependency>
@RestController
public class HelloController {
@Value("${server.port}")
private String port;
@GetMapping("/hello")
public String sayHello() {
return "Hello World, port = " + port;
}
}
注册到nacos上面,需要在主启动类上加上注解@EnableDiscoveryClient。yml配置文件如下
server:
port: 1001
spring:
application:
name: nacos-client-prodiver
cloud:
nacos:
discovery:
server-addr: localhost:8848
config:
server-addr: localhost:8848
file-extension: yaml
以上就是简单构建一下三个微服务提供者实例,接下来时消费者。消费者也需要注册到nacos上,且还需要SpringCloud相关的依赖。
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-commons</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
yml配置文件
server:
port: 8081
spring:
application:
name: nacos-client-consumer
cloud:
nacos:
discovery:
server-addr: localhost:8848
config:
server-addr: localhost:8848
file-extension: yaml
同样的,在主启动类上也需要加上@EnableDiscoveryClient注解。之前的做法是在调用接口的地方直接写WebClient.create()或者WebClient.builder().build()等,但是这次先不这么写,写一个配置类,用来注册WebClient实例,然后在要用到的地方,直接通过@Autowired注入即可。
@Configuration
public class WebClientConfig {
@Autowired
private LoadBalancerExchangeFilterFunction lbFunction;
@Bean
public WebClient webClient(){
return WebClient.builder()
.filter(lbFunction)
.build();
}
}
对于webclient来说,在这个filterChain中使用了LoadBalancerExchangeFilterFunction,LoadBalancerExchangeFilterFunction的filter方法里头,对原来的request进行了包装,使用loadBalancerClient根据服务ID进行服务发现选取可用的服务地址,然后替换原来的uri,构造成新的request传递到下一个filter,完成负载均衡的功能。
测试代码
@Autowired
private WebClient webClient;
@GetMapping("/loadBalance/hello")
public String loadBalanceHello() {
Mono<String> mono = webClient
.get()
.uri("http://nacos-client-prodiver/hello")
.retrieve()
.bodyToMono(String.class);
return mono.block();
}
通过上面的测试,其实就已经实现了负载均衡的功能,只不过改功能是在SpringCloud包下提供的,而不是直接由WebFlux提供。但是当你在编码的时候会发现,其实LoadBalancerExchangeFilterFunction这个类已经被标记为@Deprecated,且官方还提示使用ReactorLoadBalancerExchangeFilterFunction来代替。
/**
* @author Spencer Gibb
* @author Ryan Baxter
* @deprecated Use {@link ReactorLoadBalancerExchangeFilterFunction} instead.
*/
@Deprecated
public class LoadBalancerExchangeFilterFunction implements ExchangeFilterFunction {
所以看到这里,边直接将LoadBalancerExchangeFilterFunction替换为了ReactorLoadBalancerExchangeFilterFunction使用,结果当然便是启动报错。报错信息如下
***************************
APPLICATION FAILED TO START
***************************
Description:
Field lbFunction in com.icode.config.WebClientConfig required a bean of type 'org.springframework.cloud.client.loadbalancer.reactive.ReactorLoadBalancerExchangeFilterFunction' that could not be found.
The injection point has the following annotations:
- @org.springframework.beans.factory.annotation.Autowired(required=true)
The following candidates were found but could not be injected:
- Bean method 'loadBalancerExchangeFilterFunction' in 'ReactorLoadBalancerClientAutoConfiguration.ReactorLoadBalancerExchangeFilterFunctionConfig' not loaded because AnyNestedCondition 0 matched 2 did not; NestedCondition on OnNoRibbonDefaultCondition.RibbonLoadBalancerNotPresent @ConditionalOnMissingClass found unwanted class 'org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient'; NestedCondition on OnNoRibbonDefaultCondition.RibbonNotEnabled @ConditionalOnProperty (spring.cloud.loadbalancer.ribbon.enabled=false) did not find property 'spring.cloud.loadbalancer.ribbon.enabled'
Action:
Consider revisiting the entries above or defining a bean of type 'org.springframework.cloud.client.loadbalancer.reactive.ReactorLoadBalancerExchangeFilterFunction' in your configuration.
大概意思就是与Ribbon的负载均衡冲突,无法注入ReactorLoadBalancerExchangeFilterFunction,需要配置spring.cloud.loadbalancer.ribbon.enabled=false,但是并没有找到spring.cloud.loadbalancer.ribbon.enabled的配置。其实当你一开始看不懂的时候,可以去百度,但是这个问题百度上很少,笔者找到一篇描述此问题的文章https://github.com/alibaba/spring-cloud-alibaba/issues/1617或者官方文档

spring:
application:
name: nacos-client-consumer
cloud:
nacos:
discovery:
server-addr: localhost:8848
config:
server-addr: localhost:8848
file-extension: yaml
loadbalancer:
ribbon:
enabled: false
需要添加关于loadbalancer的依赖的支持。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
这样配置完了,就可以关闭Ribbon的负载均衡,成功注入ReactorLoadBalancerExchangeFilterFunction。接下来启动三个微服务提供者实例和一个消费者实例,访问http://localhost:8081/loadBalance/hello,实现负载均衡。
博客内容仅供自已学习以及学习过程的记录,如有侵权,请联系我删除,谢谢!
网友评论