SpringCloud微服务之LoadBalancer 负载均衡流程及自定义负载均衡策略

853 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第10天,点击查看活动详情

前言

注意本篇文章使用 SpringCloud2021.0.1SpringBoot2.6.5 版本,使用Eureka服务器进行服务注册与发现,主要讲述其负载均衡实现的流程和自定义负载均衡策略。

希望观众老爷们多多支持,并在评论区批评指正!

1. 负载均衡流程

1.1. 准备

我们用于发起请求调用的 restTemplate 对象(HTTP客户端请求工具) 进行配置时,对其标注 @LoadBalanced注解之后,会启用拦截器对我们发起的服务调用请求进行拦截。

通过 LoadBalancerInterceptor拦截器进行拦截,实现自 ClientHttpRequestInterceptor 接口:

LoadBalancerInterceptor拦截服务调用请求后,会获取 UriserviceName 作为 this.loadBalancer.execute() 的参数,然后执行 execute方法。

我们点进去这个方法,会转到 LoadBalancerClient接口,然后我们 Alt+Enter 进入该接口的实现:BlockingLoadBalancerClient

1.2. 具体流程

1.2.1. BlockingLoadBalancerClient 负载均衡的具体实现类

然后我们进行断点调试,查看一下具体的执行流程:假设发起请求 /borrow/1 获取用户1的借阅详细信息,那么会对 userservicebookservice 发起服务调用。我们就看一下对 userservice 远程调用的执行过程吧。

我们点击步入,进入 BlockingLoadBalancerClientexecute(String serviceId, LoadBalancerRequest<T> request)方法:

然后一直向下执行,到红色框位置:

通过创建对象LoadBalancerRequestAdapter获取负载均衡请求适配器。

然后我们一直向下执行,到红色框位置:

supportedLifecycleProcessors 用于获取支持的生命周期处理器集合,我们没有加任何处理,返回的集合为空 size = 0

1.2.2. choose() 自动选择对应的微服务实例

然后通过调用 this.choose(serviceId, lbRequest) 通过服务名称和负载均衡适配器自动选择对应的服务实例信息。


我们点击步入这个方法:

然后通过 loadBalancerClientFactory 负载均衡客户端工厂的 getInstance 方法,获取 loadBalancer 负载均衡器,也就是负载均衡策略,默认为 RoundRobinLoadBalancer(轮询分配策略,每个微服务实例依次获取请求),还有 RandomLoadBalancer(随机分配策略,每个微服务实例随机获取请求)。

显然 loadBalancer 对象不为空,然后执行 Mono.from方法根据获取到的负载均衡分配策略,获取一个 loadBalancerResponse 负载均衡的响应,也就是选择了 userservice对应的一个微服务实例的相关信息,如端口,真实地址、服务状态等。这个过程是向Eureka服务发起请求,Eureka根据分配策略,选择一个可用的对应服务,然后会返回此服务的主机地址等信息:

然后进行返回结果给 serviceInstance 对象

如果serviceInstance 对象为空,则报错提示没有对应的微服务,服务调用失败。

1.2.3. 执行 excute 的重载方法

显然 serviceInstance 对象不为空,然后执行 excute的重载方法。

先将 serviceInstance对象封装为 defaultResponse,再通过 serviceId获取支持生命周期处理器。

然后进行判断 request 对象(负载均衡请求适配器)是不是一个Request 对象。如果是,就进行转型,否则就 new 一个新的 Request 对象。

然后遍历 supportedLifecycleProcessors 支持生命周期处理器,对请求进行一些处理。

下一步进入 try 语句块,获取 response 响应对象,返回給上一级。

返回的结果

1.2.4. 返回结果,执行真正的请求

返回到上一级之后,就执行真正的请求,然后返回结果

1.3. 总结

该过程就是通过拦截器拦截我们的服务调用请求,通过微服务名称,请求 Eureka服务器获取服务的真实地址后,返回一个响应对象,包含该微服务的信息url,然后根据这个信息向该微服务发起真正的请求。

2. 自定义负载均衡策略

LoadBalancer默认提供了两种负载均衡策略:

  1. RandomLoadBalancer  -  随机分配策略
  2. (默认) RoundRobinLoadBalancer  -  轮询分配策略,请求会依次分配给每个微服务实例

2.1. 自定义负载均衡策略

现在我们希望修改默认的负载均衡策略,比如我们现在希望用户服务采用随机分配策略,我们需要先创建随机分配策略的配置类(不用加@Configuration):

public class LoadBalancerConfig {

    //将官方提供的 RandomLoadBalancer 注册为Bean
    @Bean
    public ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory){
        String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
        return new RandomLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
    }

}

然后我们需要为对应的服务指定负载均衡策略,配置在 template 对象的配置类中:

value 指定使用该负载均衡策略的服务名称,configuration 指定该负载均衡策略的具体实现类。

2.2. 测试

  1. BlockingLoadBalancerClient 中的 choose 方法添加断点,观察是否采用我们指定的策略进行请求:

果然使用了我们指定的随机分配策略了。

  1. 我们采用一种直观的方式,看发给 userservice 的请求是否是随机的

我们发现 userservice01 被调用两次,userservice02 被调用四次,证明是随机的。而默认的轮询策略则是各三次。