暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

长文cloud实战,客户端负载均衡Ribbon和LoadBalancer的前世今生大揭秘

帅哥趣谈 2021-09-09
2106

2020年以后随着Netflix的相关SpringCloud组件进入停更状态,Cloud吸收前人经验,自己创造了一套相关组件,今天就跟大家揭秘一下客户端负载均衡组件Ribbon和LoadBalancer。

现状大PK

Ribbon:

目前处于停更维护阶段,由于Ribbon比较优秀,生命力顽强,在生产环境还处在大规模使用中, 暂时还没有完全被替换,但是在2020年以后的cloud版本中已经删除了Ribbon的依赖。

LoadBalancer:

  • cloud正在大规模引入LoadBalancer,还未全部占领高地,随着时间的推移,再加上cloud的大力扶持,未来迟早有一天LoadBalancer会一统江湖。

  • 还支持响应式客户端的负载均衡,Spring Cloud LoadBalancer结合Spring Web Flux实现客户端负载均衡调用。即:WebClient。

Ribbon简介&实战

Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡算法和服务调用。Ribbon客户端组件提供一系列完善的配置项,如连接超时,重试等。简单说,就是在配置文件中列出LoadBalancer(简称LB)后面所有的机器,Ribbon会自动的帮助你基于某种规则(如轮询,随机等)去连接这些机器,很容易使用Ribbon实现自定义的负载均衡算法。

LB负载均衡是什么

  • 我给大家讲一下什么是客户端负载均衡,服务消费者从注册中心拿到服务提供者集群,自己决定使用何种算法找到目标服务,这个过程就是客户端负载均衡,即主动权掌握在自己手里

  • 相对客户端负载均衡,服务端负载均衡,就是消费者把请求交给服务端,由服务端来负责找到目标服务提供者,即主动权掌握在被人手里。

  • 我看大家还有一种分类方式:集中式LB和进程内LB,都是一个意思。

Ribbon工作流程

  1. 从注册中心获取服务列表

  2. 根据用户指定的策略,找到目标服务提供者

  3. 通过Feign或者OpenFeign,Web客户端调用工具完成用户请求 

负载均衡策略

  • RoundRobinRule:轮询

  • RandomRule:随机

  • RetryRule:先按照轮询策略获取服务,如果获取服务失败则在指定时间内进行重试,获取可用的服务

  • WeightedResponseTimeRule:对轮询的扩展,响应速度越快的实例选择权重越大,越容易被选择

  • BestAvailableRule:会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务

  • AvailabiliyFilteringRule:先过滤掉故障实例,再选择并发较小的实例

  • ZoneAvoidanceRule:默认规则,复合判断server所在区域的性能和server的可用性选择服务器

自定义负载均衡策略

  1. Ribbon官方明确规定,自动以的配置类,不能放在@ComponentScan所扫描的当前包下以及子包下,否则我们定义的配置类就会被所有的Ribbon客户端所共享,达不到特殊化定制的目的。

  2. 在某些特殊场景下,我们可能需要targetId相同,所有请求应该落到同一服务上。这个时候随机和轮询都不满足,所以就需要自定义一致性hash算法。

  3. 在某一个服务中,如果只调用一个服务提供方,可以写在@ComponentScan所扫描的包及子包下。如果是调用多个服务提供方,需要不同的策略,且互不干扰。就需要遵守第一条原则。

  4. 一般boot工程默认配置了@ComponentScan所扫描的包是启动类Application所在包及子包,所以在有时候我们不配置自动扫描也能正常运行的原因。


  • 自定义配置类,比如使用随机替换默认的轮询

    @Configuration
    public class MyRule{


    @Bean
    public IRule myRule(){
    return new RandomRule();
    }


    }


    • 在启动类上使用@RibbonClient注解,设置自定义负载均衡策略使其生效,name是服务名,即调用哪个服务集群的时候使用自定义规则。

      @SpringBootApplication
      @EnableEurekaClient
      @RibbonClient(name = "cloud-payment-service",configuration=MyRule.class)
      public class Application8080 {
      public static void main(String[] args) {
      SpringApplication.run(Application8080.class, args);
      }
      }

      LoadBalancer简介&实战

      LoadBalancer的优势

      • 跟Ribbon一样也支持RestTemplate,使用方法一样,由于cloud的其他组件还在使用Ribbon,所以在使用LoadBalancer的时候要屏蔽Ribbon。

      • LoadBalancer还支持响应式编程方式的异步访问客户端的负载均衡,依赖Spring Web Flux实现客户端负载均衡调用,底层依赖Netty的AIO

      引入LoadBalancer依赖jar包

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


        配置文件application.yml屏蔽Ribbon负载均衡

        在Ribbon和LoadBalancer依赖都引入的情况,cloud默认优先启用Ribbon,当我们想使用LoadBalancer的时候就要先屏蔽到Ribbon
          spring:
          cloud:
          loadbalancer:
          ribbon:
          enabled: false


          业务类和启动类都没有区别,还是使用RestTemplate作为客户端来完成服务调用,此方式跟Ribbon在编码方面没什么区别。是Ribbon还是LoadBalancer,主要体现在jar包引入和yml文件关闭默认Ribbon。

            //配置RestTemplate
            @Configuration
            public class ApplicationConfig {


            @Bean
            @LoadBalanced
            public RestTemplate getRestTemplate(){
            return new RestTemplate();
            }


            }


            //controller部分
            @RestController
            @RequestMapping("/consumer")
            public class ConsumerCtrol {
            private static String PAYMENTURL = "http://CLOUD-PAYMENT-SERVICE";
            @Resource
            private RestTemplate restTemplate;


            @GetMapping("/getPayment/{id}")
            public MsgResponseBody getPaymet(@PathVariable("id") Long id){
            System.out.println("adada");
            return restTemplate.getForObject(PAYMENTURL+"/payment/getPayment/"+id+"?userName=123", MsgResponseBody.class);
            }
            }

            WebClient方式发起服务调用

            引入webflux的相关依赖

              <dependency>
              <!-- Spring Webflux响应式web编程-->
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-webflux</artifactId>
              </dependency>

              配置类中声明要注入的WebClient.Builder,是WebClient的内部类,也是编程的入口

                //配置RestTemplate
                @Configuration
                public class ApplicationConfig {

                @Bean
                @LoadBalanced
                public RestTemplate getRestTemplate(){
                return new RestTemplate();
                }

                @Bean
                @LoadBalanced
                public WebClient.Builder builder() {
                return WebClient.builder();
                }

                }

                //controller部分
                @RestController
                @RequestMapping("/consumer")
                public class ConsumerCtrol {
                private static String PAYMENTURL = "http://CLOUD-PAYMENT-SERVICE";
                @Resource
                private RestTemplate restTemplate;

                @GetMapping("/getPayment/{id}")
                public MsgResponseBody getPaymet(@PathVariable("id") Long id){
                System.out.println("adada");
                return restTemplate.getForObject(PAYMENTURL+"/payment/getPayment/"+id+"?userName=123", MsgResponseBody.class);
                }

                @GetMapping("/getPayment/webClient/{id}")
                public Mono<MsgResponseBody> getPaymetByWebClient(@PathVariable("id") Long id) {
                System.out.println("bbbbbbb");
                //return restTemplate.getForObject(PAYMENTURL + "/payment/getPayment/" + id + "?userName=123", MsgResponseBody.class);
                return clientBuilder.baseUrl(PAYMENTURL).build().get().uri("/payment/getPayment/" + id + "?userName=123").retrieve().bodyToMono(MsgResponseBody.class);
                }

                }

                默认跟Ribbon是一样的,都是轮询策略,自定义配置使用的是@LoadBalancerClient,用法跟Ribbon的@RibbonClient一样。

                在Springcloud alibaba的2.2.6版本对应的Spring-cloud版本是Hoxton.SR9,而spring-cloud-loadbalancer2.2.6版本负载均衡策略仅支持轮询策略。

                Hoxton.SR10版本以后增加了随机负载均衡策略


                切换随机负载均衡策略,新建两个Module分别绑定8004和8005端口,服务名是CLOUD-PAYMENT-SERVICE1,使用轮询策略,上面说了,MyLoadBalancerRandomConfig要放在@ComponentScan不能扫描到的package中。

                  @Configuration
                  public class MyLoadBalancerRandomConfig {

                  @Bean
                  public ReactorLoadBalancer<ServiceInstance> reactorServiceInstanceLoadBalancer(Environment environment,
                  LoadBalancerClientFactory loadBalancerClientFactory) {
                  String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
                  return new RandomLoadBalancer(
                  loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
                  }
                  }

                  启动类配置@LoadBalancerClient,配置原来的8001和8002,CLOUD-PAYMENT-SERVICE服务使用随机策略

                    @SpringBootApplication
                    @EnableEurekaClient
                    @LoadBalancerClient(value = "CLOUD-PAYMENT-SERVICE", configuration = MyLoadBalancerRandomConfig.class)
                    public class Application6060 {
                    public static void main(String[] args) {
                    SpringApplication.run(Application6060.class,args);
                    }
                    }

                    controller类的修改,getPayment对应轮询,getPaymentWebClient对应随机

                      @RestController
                      @RequestMapping("/consumer")
                      public class ConsumerCtrol {
                      private static final String PAYMENTURL = "https://CLOUD-PAYMENT-SERVICE";//随机
                      private static final String PAYMENTURL2 = "https://CLOUD-PAYMENT-SERVICE1";//轮询
                      @Autowired
                      private RestTemplate restTemplate;
                      @Autowired
                      private WebClient.Builder clientBuilder;

                      @GetMapping("/getPayment/{id}")
                      public MsgResponseBody getPaymet(@PathVariable("id") Long id) {
                      System.out.println("adada");
                      return restTemplate.getForObject(PAYMENTURL2 + "/payment/getPayment/" + id + "?userName=123", MsgResponseBody.class);
                      }

                      @GetMapping("/getPayment/webClient/{id}")
                      public Mono<MsgResponseBody> getPaymetByWebClient(@PathVariable("id") Long id) {
                      System.out.println("bbbbbbb");
                      //return restTemplate.getForObject(PAYMENTURL + "/payment/getPayment/" + id + "?userName=123", MsgResponseBody.class);
                      return clientBuilder.baseUrl(PAYMENTURL).build().get().uri("/payment/getPayment/" + id + "?userName=123").retrieve().bodyToMono(MsgResponseBody.class);
                      }
                      }

                      至此,Ribbon和LoadBalancer两种方式的负载均衡策略就给大家介绍完了。但是同学们要明白,仅靠这两种并不能覆盖所有场景,只能说符合一般场景,有些特殊场景,比如要求落点要根据targetID去负载,相同的targetID的多次请求要落到同一点上,这是前面两种都不合适,一致性Hash就比较合适,后面单独写一篇自定义一致性Hash的负载均衡策略实现,大家敬请期待。

                      --END--

                      ↑ ↑ ↑点击上方 名片,关注 帅哥趣谈

                      回复 cloud

                      获取源码地址

                      shishuai860505),拉大家进群,一起交流学习

                      认真传播技术,真诚结交朋友


                      文章转载自帅哥趣谈,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                      评论