springcloud-Ribbon负载均衡服务调用(四)

Ribbon入门介绍

  • 是什么
  1. Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡工具.
  2. Ribbon主要功能是提供客户端的软件负载均衡算法和服务调用. 
  3. Ribbon客户端组件提供一系列完整的配置项如连接超时, 重试等. 简单的说, 就是在配置文件中列出Load Balancer后所有的机器, Ribbon会自动帮我们基于某种规则(如简单轮询, 随机连接)去链接这些机器, 我们很容易使用Ribbon实现自定义的负载均衡算法.
  • 负载均衡
  1. 简单的说就是将用户的请求平摊到多个服务上, 从而达到系统的高可用, 常见的负载均衡有软件Nginx, LVS, 硬件F5等.
  2. Ribbon负载均衡 VS Nginx负载均衡
    • Nginx是服务器负载均衡, 客户端所有请求都会交给nginx, 然后由nginx实现转发请求, 即负载均衡是由服务端实现的.
    • Ribbon是客户端负载均衡, 在调用微服务接口的时候, 会在注册中心上获取注册信息服务列表之后缓存到JVM本地, 从而在本地实现RPC远程服务调用技术.
  3. 集中式LB
    • 在服务的消费方和提供方之间使用独立的LB设施(如Nginx), 由该设施负责把访问请求通过某种策略转发至服务的提供方.
  4. 进程内LB
    • 将LB逻辑集成到消费方, 消费方从服务注册中心获知哪些地址可用, 然后自己再从这些地址中选出一个合适的服务器.
    • Ribbon就属于进程内LB, 它只是一个类库, 集成于消费方进程, 消费方通过它来获取到服务提供方的地址.

Ribbon负载均衡演示

  • 所谓Ribbon, 简单的说就是负载均衡 + RestTemplate
  • 架构说明

  1. Ribbon在工作时分两步
    • 第一步先选择Eureka Server, 它优先选择在同一个区域内负载较少的server.
    • 第二步再根据用户指定的策略, 在从server取到的服务注册列表中选择一个地址.
      • 策略有多种可选, 如轮询, 随机, 根据响应时间加权.
  2. 这里需要注意, Ribbon其实就是一个软负载均衡的客户端组件, 它可以和其他所需请求的客户端结合使用, 这里仅用Eureka举例.
  • POM分析
  1. 我们只加入了eureka的依赖
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
  2. 而在Eureka中是有Ribbon的依赖的
        <dependency>
          <groupId>org.springframework.cloud</groupId>
          <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
          <version>2.2.1.RELEASE</version>
          <scope>compile</scope>
        </dependency>
  • 详细讲解RestTemplate

Ribbon核心组件IRule

  • IRule接口: 根据特定算法从服务列表中选取一个要访问的服务
  1. 有7种负载均衡策略
  • 切换负载均衡策略
  1. 我们要写的自定义配置类不能放在@ComponentScan所扫描的当前包下以及子包下, 否则我们自定义的配置类就会被所有的Ribbon客户端共享, 达不到特殊化定制的目的了.
  2. 我们新建package及配置类
  3. 配置类代码
    @Configuration
    public class MySelfRule {
    
        @Bean
        public IRule myRule() {
            //将负载均衡策略改为随机
            return new RandomRule();
        }
    }
  4. 主启动类上要加@RibbonClient注解
    • name属性: 服务提供者(负载均衡请求的接收方)
    • configuration属性: 指明我们的配置类
      @RibbonClient(name = "CLOUD-PAYMENT-SERVICE", configuration = MySelfRule.class)
      @EnableEurekaClient
      @SpringBootApplication
      public class OrderMain80 {
      
          public static void main(String[] args) {
              SpringApplication.run(OrderMain80.class, args);
          }
      }
  5. 测试: http://localhost/consumer/payment/get/1

Ribbon负载均衡算法

  • 负载轮询算法原理
  1. rest接口第几次请求数 % 服务器集群总数量 = 实际调用服务器位置下标.
    • 集群总数量: 2台. (List = 2 instance)
    • 第一次: 1 % 2 = 1
    • 第二次: 2 % 2 = 0
    • 第三次: 3 % 2 = 1
    • ......
    • 如果重启了, 那么会从1重新开始.
  • 手写一个负载轮询算法
  1. 在8001/8002微服务的controller添加如下方法
        @GetMapping("/payment/lb")
        public String getPaymentLB() {
            return serverPort;
        }
  2. 80订单微服务改造
    • 去掉@LoadBalanced注解
    • LoadBalancer接口
      public interface LoadBalancer {
      
          ServiceInstance instances(List<ServiceInstance> serviceInstances);
      }
    • MyLB
      @Component
      public class MyLB implements LoadBalancer {
      
          private AtomicInteger atomicInteger = new AtomicInteger(0);
      
          public final int getAndIncrement() {
              int current;
              int next;
              do {
                  current = this.atomicInteger.get();
                  next = current >= 2147483000 ? 0 : current+1;
              } while(!this.atomicInteger.compareAndSet(current, next));
              return next;
          }
      
          @Override
          public ServiceInstance instances(List<ServiceInstance> serviceInstances) {
      
              int index = getAndIncrement() % serviceInstances.size();
      
              return serviceInstances.get(index);
          }
      }
    • controller方法
          @Resource
          private RestTemplate restTemplate;
      
          @Resource
          private LoadBalancer loadBalancer;
      
          @Resource
          private DiscoveryClient discoveryClient;
      
          @GetMapping("/consumer/payment/lb")
          public String getPaymentLB() {
              List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
              if(instances == null || instances.size() <= 0) {
                  return null;
              }
              ServiceInstance serviceInstance = loadBalancer.instances(instances);
              URI uri = serviceInstance.getUri();
              return restTemplate.getForObject(uri+"/payment/lb", String.class);
          }
  3. 测试: http://localhost/consumer/payment/lb
posted @ 2020-06-27 18:18  yellowstreak  阅读(186)  评论(0编辑  收藏  举报