WebClient对负载均衡的支持

2021-01-26  本文已影响0人  因你而在_caiyq

原创文章,转载请注明原文章地址,谢谢!

在之前的一文中介绍了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,实现负载均衡。

博客内容仅供自已学习以及学习过程的记录,如有侵权,请联系我删除,谢谢!

上一篇 下一篇

猜你喜欢

热点阅读