开发者

Spring Cloud Loadbalancer服务均衡负载器详解

目录
  • Spring Cloud Loadbalancer
    • 1、简介
    • 2、实现原理
    • 4、使用细节
    • 5、切换负载均衡算法
    • 6、缓存方案
    • 7、 生命周期
  • 总结

    Spring Cloud Loadbalancer

    1、简介

    Spring Cloud LoadBalancer 是 Spring Cloud 中提供客户端负载均衡的组件之一,它的主要作用是在服务消费者和服务提供者之间实现负载均衡。

    在微服务架构中,服务提供者往往js会有多个实例,这些实例可以部署在不同的节点或者不同的地理位置。而服务消费者需要从这些实例中选择⼀个来处理请求,以避免单个实例过载或故障导致服务不可用。这时候就需要⼀个负载均衡组件来决定将请求分发到哪个实例上。Spring Cloud LoadBalancer 封装了负载均衡算法,并提供了 LoadBalancerClient 接口,通过这个接口,应用程序可以根据负载均衡策略从可用的服务实例列表中选择⼀个实例来处理请求。

    与传统的负载均衡器相比,Spring Cloud LoadBalancer 的优点在于它是⼀个基于应用程序的负载均衡器,可以与 Spring Cloud 中的其他组件集成,如服务注册中心、断路器、配置中心等,从而实现更灵活、更高效的微服务架构。

    Spring Cloud 2020版本以后,默认移除了对Netflix的依赖,其中就包括Ribbon,官⽅默认推荐使用Spring Cloud Loadbalancer正式替换Ribbon,并成为了Spring Cloud负载均衡器的唯⼀实现。

    2、实现原理

    第1次访问:

    Spring Cloud Loadbalancer服务均衡负载器详解

    节点数量状态变化主动推送消费者实例更新本地节点数据

    Spring Cloud Loadbalancer服务均衡负载器详解

    3、模拟实现

    在本节中,我们使用article-service和video-service来模拟服务间的负载均衡。

    服务提供者:

    这里我们使用video-service作为服务提供者,直接开启三个video-service模拟服务集群来提供服务

    Spring Cloud Loadbalancer服务均衡负载器详解

    按照以上步骤,我们直接在本地的8001、8002、8003端口上配置了三个服务提供者video-service,然后逐一启动。python

    Spring Cloud Loadbalancer服务均衡负载器详解

    服务消费者:

    在消费者端引入依赖

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-loadbalancer</artifactId>
        <version>3.1.3</version>
    </dependency>

    Java Config实例化RestTemplate对象,并使用@LoadBalanced声明负载均衡当使用负载均衡器来调用多个服务实例时,可以使用 @Balanced 注解标记 RestTemplate 或WebClient 实例。这会自动启用负载均衡功能,从而使请求在多个服务实例之间进行分布。

    在使用@Balanced 注解时,需要确保在应用程序中已配置了负载均衡器(如 Ribbon 或 Spring CloudLoadBalancer)。同时,需要确保 RestTemplate 或 WebClient 已经配置为使用负载均衡器。

    这里就不书写配置类,直接在启动类中向IoC容器注入RestTemplate对象

    @SpringBootApplication
    @EnableFeignClients //开启OpenFeign实现服务间REST通信
    public class ArticleServiceApplication {
    
        @Bean
        @LoadBalanced
        public RestTemplate restTemplate(){
            return new RestTemplate();
        }
    
        public static void main(String[] args) {
            SpringApplication.run(ArticleServiceApplication.class, args);
        }
    
    }

    在使用时注入restTemplate对象,利用RestTemplate对象发起HTTP请求,注意:请求IP地址的地方此时改为要远程访问的服务名,如:video-service。

    @RestController
    public class ArticleController {
        @Resource
        private ArticleService articleService;
    
        @GetMapping("/list")
        public List<Article> list(){
            return articleService.list();
        }
    
        @Resource
        private RestTemplate restTemplate;
        
        @GetMapping("/remote-call")
        public String remoteCall(){
            String result = restTemplate.getFo编程客栈rObject("http://video-service/video?articleId=1648", String.class);
            return "remote:" + result;
        }
    
    }

    4、使用细节

    Spring Cloud提供了自己的客户端负载均衡器抽象和实现。为了实现负载均衡机制,新增了ReactiveLoadBalancer接口,并提供了基于轮询和随机的实现。

    为了从反应式的ServiceInstanceListSupplier中获取实例进行选择,我们使用了⼀个ServiceInstanceListSupplier的基于服务发现的实现,该实现使用了类路径下的Discovery Client来检索可用实例。

    5、切换负载均衡算法

    默认情况下使用的ReactiveLoadBalancer实现是RoundRobinLoadBalancer。要切换到不同的实现,无论是选择性的服务还是全部服务,您都可以使用自定义的LoadBalancer配置机制。

    例如,可以通过@LoadBalancerClient注释传递以下配置,以切换到使用RandomLoadBalancer:

    package com.dw.config;
    
    import org.springframework.cloud.client.ServiceInstance;
    import org.springframework.cloud.loadbalancer.core.RandomLoadBalancer;
    import org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer;
    import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
    import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
    import org.springframework.context.annotation.Bean;
    import org.springframework.core.env.Environment;
    
    
    public class CustomLoadBalancerConfiguration {
        @Bean
        ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment,
                                                                LoadBalancerClientFactory loadBalancerClientFactory) {
            String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
            return new RandomLoadBalancer(loadBalancerClientFactory
                    .getLazyProvider(name, ServiceInstanceListSupplier.class), name);
        }
    }

    注意:您作为 @ LoadBalancerClient或 @ LoadBalancerClients配置参数传递的类不应使用@configuration进⾏注释,或者在组件扫描范围之外。

    应用代码:

    @SpringBootApplication
    @LoadBalancerClients({
            @LoadBalancerClient(value = "video-service",
            configuration = CustomLoadBalancerConfiguration.class)
    })
    @EnableFeignClients //开启OpenFeign实现服务间REST通信
    public class ArticleServiceApplication {
    
        @Bean
        @LoadBalanced
        public RestTemplate restTemplate(){
            return new RestTemplate();
        }
    
        public static void main(String[] args) {
            SpringApplication.run(ArticleServiceApplication.class, args);
        }
    
    }

    应用程序可以使用 Spring Cloud 提供的 LoadBalancerClient 对象来发起对 "video-service" 服务的调用,并自动进行负载均衡选择实例。

    6、缓存方案

    除了基本的 ServiceInstanceListSupplier 实现每次需要选择实例时都通过 DiscoveryClient 检索实例之外,我们提供了两种缓存实现,以提高效率:

    • Caffeine-backed LoadBalancer缓存实现
    • 默认 LoadBalancer 缓存实现

    如果在类路径中没有 Caffeine,则会使用 DefaultLoadBalancerCache。它自动随着 spring-cloud-starter-loadbalancer 提供。

    如果要使用 Caffeine 而不是默认缓存,请将 com.github.ben-manes.caffeine:caffeine 依赖项添加到类路径中。

    LoadBalancer 缓存配置

    您可以通过将符合 Spring Boot 字符串到 Duration 转换器语法的字符串作为spring.cloud.loadbalancer.cache.ttl 属性的值,设置自己的 ttl 值(写入后过多长时间条目应过期),以 Duration 表示。您还可以通过设置 spring.cloud.loadbalancer.cache.capacity 属性的值,设置自己的 LoadBalancer 缓存初始容量。

    默认设置包括将 ttl 设置为 35 秒,而默认的 initialCapacity 为 256。

    在 Spring Cloud LoadBalancer 中,为了减少对服务注册中心的频繁调用,提⾼服务调用的性能,LoadBalancer 会缓存服务实例列表。当⼀个请求需要访问某个服务时,LoadBalancer 会先从缓存中查找可用的服务实例列表,如果缓存中没有可用的实例列表,则会重新从服务注册中心获取服务实例列表。因此,调整 spring.cloud.loadbalancer.cache.capacity 的值可以影响 LoadBalancer 缓存的大小,从而影响缓存命中率和性能。

    上面的配置将负载均衡器的缓存容量限制为 100 个实例。当缓存容量达到上限时,新的实例将覆盖旧的实例。这样,缓存将始终包含最新的实例列表,并且不会占用过多的内存资源。

    我们还可以通过将 spring.cloud.loadbalancer.cache.enabled 的值设置为 false,完全禁用LoadBalancer 缓存。

    虽然基本的非缓存实现对于原型设计和测试很有用,但比缓存版本要低效得多,因此我们建议在生产中始终使用缓存版本。如果发现已经由 DiscoveryClient 实现(例如 EurekaDiscoveryClient)进行了缓存,则应禁用负载平衡器缓存以防止双重缓存。

    spring.cloud.loadbalancer.cache.enabled=true
    spring.cloud.loadjavascriptbalancer.cache.ttl=120s
    spring.cloud.loadbalancer.cache.capacity=100

    7、 生命周期

    使用自定义 LoadBalancer 配置注册的⼀种有⽤的 bean 类型是 LoadBalancerLifecycle。

    LoadBalancerLifecycle bean 提供了回调⽅法,命名如下:

    onStart(Request<RC> request)
    onStartRequest(Request<RC> request, Response<T> lbResponse)
    onComplete(CompletionContext<RES, T, RC> completionContext)

    我们要实现这些方法来指定负载均衡前后应该发⽣的操作。

    onStart(Request<RC> request)

    以 Request 对象作为参数。它包含用于选择适当实例的数据,包括下游客户端请求和提示。

    onStartRequest 也以 Request 对象作为参数,此外还有 Response<T> 对象。

    另一方面,完成时提供了 CompletionContext 对象给

    onComplete(CompletionContext<RES, T, RC>completionContext) 

    方法。它包含 LoadBalancer 响应,包括所选服务实例、针对该服务实例执行的请求的状态,(如果有)返回给下游客户端的响应,以及(如果发生异常)相应的 Throwable。

    supports(Class requestContextClass, Class responseClass, Class serverTypeClass) 

    方法可用于确定所讨论的处理器是否处理提供的类型的对象。如果未被⽤户覆盖,则它返回 true。

    案例:

    @Component
    @Slf4j
    public class CustomLoadBalancerLifecycle implements LoadBalancerLifecycle
    {
        @Override
        public void onStart(Request request) {
            log.info("》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》");
            log.info("Starting load balancing for request: javascript" + request);
        }
        @Override
        public void onStartRequest(Request request, Response lbResponse) {
            log.info("Selected service instance: " + lbResponse.getServer());
        }
        @Override
        public void onComplete(CompletionContext completionContext) {
            // 在负载均衡之后执⾏的操作
            log.info("Load balancing completed for request: " + completionContext.getLoadBalancerRequest() +
                            ", with status: " + completionContext.status());
            log.info("《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《《");
        }
    }

    执行http://localhost:8101/remote-call,观察输出日志.

    总结

    以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程客栈(www.devze.com)。

    0

    上一篇:

    下一篇:

    精彩评论

    暂无评论...
    验证码 换一张
    取 消

    最新开发

    开发排行榜