• 「SpringCloud」04 Ribbon负载均衡服务调用


    SpringCloud—Ribbon负载均衡服务调用

    笔记整理自【尚硅谷】周阳SpringCloud框架开发教程

    ribbon
    image-20220801123922304

    1. 概述

    Ⅰ. Ribbon是什么

    Spring Cloud Ribbon是基于Netflix Ribbon实现的一套 客户端 负载均衡的工具。

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

    Ribbon github

    目前也进入维护模式,未来替换方案:Spring Cloud Loadbalancer

    image-20220805115835877

    Ⅱ. Ribbon能做什么

    LB(负载均衡)

    LB负载均衡 (Load Balance) 是什么

    简单的说就是将用户的请求平摊的分配到多个服务上,从而达到系统的HA(高可用)

    常见的负载均衡有软件Nginx,LVS,硬件 F5等。

    Ribbon本地负载均衡客户端 VS Nginx服务端负载均衡区别

    Nginx是服务器负载均衡,客户端所有请求都会交给nginx,然后由nginx实现转发请求。即负载均衡是由服务端实现的。

    Ribbon本地负载均衡,在调用微服务接口时候,会在注册中心上获取注册信息服务列表之后缓存到JVM本地,从而在本地实现RPC远程服务调用技术。

    • 集中式LB

      ➢ 即在服务的消费方和提供方之间使用独立的LB设施(可以是硬件,如F5, 也可以是软件,如nginx),由该设施负责把访问请求通过某种策略转发至服务的提供方。

    • 进程内LB

      ➢ 将LB逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选择出一个合适的服务器。

      Ribbon就属于进程内LB,它只是一个类库,集成于消费方进程,消费方通过它来获取到服务提供方的地址。

    一句话:Ribbon = 负载均衡 + RestTemplate调用

    2. Ribbon负载均衡演示

    Ⅰ. 架构说明

    Ribbon在工作时分成两步:

    • 第一步先选择 EurekaServer,它优先选择在同一个区域内负载较少的server。
    • 第二步再根据用户指定的策略,在从server取到的服务注册列表中选择一个地址。
    • 其中Ribbon提供了多种策略:比如轮询、随机和根据响应时间加权。

    image-20220805123759055

    总结:Ribbon其实就是一个软负载均衡的客户端组件,他可以和其他所需请求的客户端结合使用,和eureka结合只是其中的一个实例。

    Ⅱ. POM

    之前写样例时候没有引入 spring-cloud-starter-ribbon 也可以使用 Ribbon

    <dependency>
        <groupId>org.springframework.cloudgroupId>
        <artifactId>spring-cloud-starter-netflix-ribbonartifactId>
    dependency>
    
    • 1
    • 2
    • 3
    • 4

    猜测 spring-cloud-starter-netflix-eureka-client 自带了 spring-cloud-starter-ribbon 引用

    证明如下:可以看到 spring-cloud-starter-netflix-eureka-client 确实引入了 Ribbon

    image-20220805123915716

    Ⅲ. 二说RestTemplate的使用

    RestTemplate官网

    image-20220805133618690

    • getForObject() / getForEntity()

      getForObject()返回对象为响应体中数据转化成的对象,基本上可以理解为Json(推荐)

      image-20220805133910322

      getForEntity()返回对象为ResponseEntity对象,包含了响应中的一些重要信息,比如响应头、响应状态码、响应体等

      image-20220805133921104

    • postForObject() / postForEntity()

      image-20220805133935546

    3. Ribbon核心组件IRule

    Ⅰ. IRule:根据特定算法中从服务列表中选取一个要访问的服务

    IRule是一个接口,其源码如下:

    package com.netflix.loadbalancer;
    
    /**
     * Interface that defines a "Rule" for a LoadBalancer. A Rule can be thought of
     * as a Strategy for loadbalacing. Well known loadbalancing strategies include
     * Round Robin, Response Time based etc.
     * 
     * @author stonse
     * 
     */
    public interface IRule{
        /*
         * choose one alive server from lb.allServers or
         * lb.upServers according to key
         * 
         * @return choosen Server object. NULL is returned if none
         *  server is available 
         */
    
        public Server choose(Object key);
        
        public void setLoadBalancer(ILoadBalancer lb);
        
        public ILoadBalancer getLoadBalancer();    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    以下是IRule接口的部分实现,这些实现分别对应了若干负载均衡算法:

    image-20220805135512154

    以下简要说明7种主要的负载均衡算法,这些负载均衡算法均是抽象类 com.netflix.loadbalancer.AbstractLoadBalancerRule 的实现,而给抽象类实现了IRule接口

    • com.netflix.loadbalancer.RoundRobinRule:轮询,为默认的负载均衡算法。
    • com.netflix.loadbalancer.RandomRule:随机。
    • com.netflix.loadbalancer.RetryRule:先按照RoundRobinRule(轮询)的策略获取服务,如果获取服务失败则在指定时间内进行重试,获取可用的服务。
    • com.netflix.loadbalancer.WeightedResponseTimeRule:对RoundRobinRule的扩展,响应速度越快的实例选择权重越大,越容易被选择。
    • com.netflix.loadbalancer.BestAvailableRule:先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务。
    • com.netflix.loadbalancer.AvailabilityFilteringRule:先过滤掉故障实例,再选择并发较小的实例。
    • com.netflix.loadbalancer.ZoneAvoidanceRule:默认规则,复合判断Server所在区域的性能和Server的可用性选择服务器。

    Ⅱ. 如何替换

    • 修改cloud-consumer-order80

    • 注意配置细节

      ➢ 官方文档明确给出了警告:

      这个自定义配置类不能放在@ComponentScan所扫描的当前包下以及子包下,

      否则我们自定义的这个配置类就会被所有的Ribbon客户端所共享,达不到特殊化定制的目的了。

      image-20220805175516942

    • 新建package

      com.atguigu.myrule

    • 上面包下新建MySelfRule规则类

      image-20220805181212848

      image-20220805181310881

    • 主启动类添加@RibbonClient

      image-20220805181526141

    • 测试

      http://localhost/consumer/payment/get/31

      如图我们用服务消费方访问服务提供方的微服务时,8001和8002不再交替轮询访问,而是随机访问:

      2

    4. Ribbon负载均衡算法

    Ⅰ. 默认负载均衡算法(轮询)原理

    负载均衡算法:Rest接口第几次请求数 % 服务器集群总数量 = 实际调用服务器位置下标,每次服务重启后Rest接口计数从1开始。

    List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE"); // list.size() = 2
    
    • 1

    根据服务方的服务名,获取其所有实例,如有以下实例:

    List[0] instancesList[1] instances
    服务名payment8001payment8002
    服务地址127.0.0.1:8001127.0.0.1:8002

    8001 + 8002 8001 + 8002 8001+8002 组合成一个集群,共计 2 2 2 台机器,集群总数为 2 2 2,按照轮询负载均衡算法原理:

    • 请求总数为 1 1 1 时, 1   %   2 = 1 1 \ \%\ 2 = 1 1 % 2=1,对应下标位置是 1 1 1,获得服务地址127.0.0.1:8001
    • 请求总数为 2 2 2 时, 2   %   2 = 0 2 \ \% \ 2 = 0 2 % 2=0,对应下标位置是 0 0 0,获得服务地址127.0.0.1:8002
    • 请求总数为 3 3 3 时, 3   %   2 = 1 3 \ \%\ 2 = 1 3 % 2=1,对应下标位置是 1 1 1,获得服务地址127.0.0.1:8001
    • 依次类推…

    Ⅱ. 源码分析

    package com.netflix.loadbalancer;
    
    import com.netflix.client.config.IClientConfig;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import java.util.List;
    import java.util.concurrent.atomic.AtomicInteger;
    
    /**
     * The most well known and basic load balancing strategy, i.e. Round Robin Rule.
     *
     * @author stonse
     * @author Nikos Michalakis 
     *
     */
    public class RoundRobinRule extends AbstractLoadBalancerRule {
    
        private AtomicInteger nextServerCyclicCounter;
        private static final boolean AVAILABLE_ONLY_SERVERS = true;
        private static final boolean ALL_SERVERS = false;
    
        private static Logger log = LoggerFactory.getLogger(RoundRobinRule.class);
    
        // 构造方法
        public RoundRobinRule() {
            nextServerCyclicCounter = new AtomicInteger(0); // 返回原子整型类 初始值为0
        }
    
        public RoundRobinRule(ILoadBalancer lb) {
            this();
            setLoadBalancer(lb);
        }
        
    	// choose():选择哪一个负载均衡的算法
        public Server choose(ILoadBalancer lb, Object key) {
            if (lb == null) {
                log.warn("no load balancer");
                return null;
            }
    
            Server server = null;
            int count = 0;
            while (server == null && count++ < 10) {
                // 获得还活着的健康的服务实例(机器)即可达的,也就是Status为up的实例
                List<Server> reachableServers = lb.getReachableServers();
                // 获取所有服务实例,无论是死是活,只要注册进服务中心即可
                List<Server> allServers = lb.getAllServers();
                // Status为up的服务实例数量
                int upCount = reachableServers.size();
                // 所有服务实例的数量,对应上述原理分析中的服务器集群总数量
                int serverCount = allServers.size();
    
                // 如果没有可达的服务实例的话,直接log警告
                if ((upCount == 0) || (serverCount == 0)) {
                    log.warn("No up servers available from load balancer: " + lb);
                    return null;
                }
    
                // 调用服务器位置下标 = incrementAndGetModulo(服务器集群总数)
                int nextServerIndex = incrementAndGetModulo(serverCount);
                server = allServers.get(nextServerIndex); // 根据下标获取服务实例
    
                if (server == null) {
                    /* Transient. */
                    Thread.yield();
                    continue;
                }
    
                if (server.isAlive() && (server.isReadyToServe())) {
                    return (server);
                }
    
                // Next.
                server = null;
            }
    
            if (count >= 10) {
                log.warn("No available alive servers after 10 tries from load balancer: "
                        + lb);
            }
            return server;
        }
    
        /**
         * Inspired by the implementation of {@link AtomicInteger#incrementAndGet()}.
         *
         * @param modulo The modulo to bound the value of the counter.
         * @return The next value.
         */
        private int incrementAndGetModulo(int modulo) {
            // 自旋锁
            for (;;) {
                // 轮询算法
                int current = nextServerCyclicCounter.get();
                int next = (current + 1) % modulo;
                if (nextServerCyclicCounter.compareAndSet(current, next)) // CAS
                    return next;
            }
        }
    
        @Override
        public Server choose(Object key) {
            return choose(getLoadBalancer(), key);
        }
    
        @Override
        public void initWithNiwsConfig(IClientConfig clientConfig) {
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110

    Ⅲ. 自己实现轮询负载均衡算法

    • 7001/7002集群启动

    • 8001/8002微服务改造

      controller

      image-20220805193922757

    • 80订单微服务改造

      ➢ ApplicationContextBean去掉注解@LoadBalanced

      image-20220805194421902

      ➢ LoadBalancer接口

      public interface LoadBalancer {
          ServiceInstance instances(List<ServiceInstance> serviceInstances);
      }
      
      • 1
      • 2
      • 3

      ➢ MyLB

      @Component
      public class MyLB implements LoadBalancer {
      
          private AtomicInteger atomicInteger = new AtomicInteger(0); // 初始值为0
      
          // 拿到访问次数
          public final int getAndIncrement() {
              int current;
              int next;
              // 自旋锁
              do {
                  current = this.atomicInteger.get();
                  next = current >= Integer.MAX_VALUE ? 0 : current + 1;
              } while(!this.atomicInteger.compareAndSet(current, next)); // CAS
              System.out.println("******第几次访问,次数next: " + next);
              return next;
          }
      
          /**
           * 从服务列表中用轮询负载均衡算法选择出具体的实例
           * Rest接口第几次请求数 % 服务器集群总数量 = 实际调用服务器位置下标
           * @param serviceInstances
           * @return
           */
          @Override
          public ServiceInstance instances(List<ServiceInstance> serviceInstances) {
              int index = getAndIncrement() % serviceInstances.size();
              return serviceInstances.get(index);
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30

      ➢ OrderController

      image-20220805210153860

      image-20220805210214818

      ➢ 测试

      http://localhost/consumer/payment/lb

      4
      image-20220805211119710

  • 相关阅读:
    Node简介
    记录一次Linux挂载NAS共享的SMB文件系统经历
    CAD一键添加审图批注、AUTOCAD——图形界线怎么设置
    服务器编程基本框架
    全量知识系统 程序详细设计 三次模型:数据模型图算模型和统计模型(Q&A百度文库)
    多台电脑之间共享、传输文件数据:不借助数据线与软件的方法
    教你微信小程序商城搭建-技术文章
    2023贵州师范大学计算机考研信息汇总
    C语言中的文件操作函数
    复制交易为什么用经纪商信号?anzo capital昂首资本3点理由心服口服
  • 原文地址:https://blog.csdn.net/weixin_53407527/article/details/126199708