SpringCloud-2.0-周阳(8. 负载均衡 - Ribbon)学习笔记

上一篇 :7. 服务注册发现 - Consul

下一篇 : 9. 负载均衡 - OpenFeign

1. 概述

1.1 Ribbon 是什么

  • SpringCloud Ribbon 是基于 Netflix Ribbon 实现的一套客户端负载均衡的工具
  • 主要功能是提供客户端的软件负载均衡算法,将 Netflix 的中间服务处连接在一起。
  • Ribbon 的客户端组件提供一系列完整的配置项,如:连接超时、重试等。简单的说就是在配置文件中列出 LoadBalance (LB:负载均衡)后面所有的机器,Ribbon 会自动帮你基于某种规则去连接这些机器

在这里插入图片描述

1.2 负载均衡(LB)

  • 核心作用:就是将用户的请求平摊在多个服务器上,从而达到系统的 High Availability(HA :高可用)

  • 常见的负载均衡软件有 Nginx、Lvs 等

  • Bubbo 和 SpringCloud 都提供了负载均衡,SpringCloud 的负载均衡算法可以自定义

  • 负载均衡算法实现的方式:轮询、权重、随机

  • 负载均衡的简单分类:
    1、集中式

    在服务的 消费者 和 提供者之间使用独立的 LB 设施,如Nginx(反向代理服务器) ,由该设施负载把访问请求通过某种策略发送至服务的提供者

    2、进程式

    将LB逻辑集成到 消费者,消费者 从服务注册中心得到哪些地址可用,然后自己再从这些地址中选出一个合适服务器

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

1.3 架构说明

  • Ribbon其实就是一个软负载均衡的客户端组件,他可以和其他所需请求的客户端结合使用,和eureka结合只是其中的一个实例。
  • Ribbon 在工作时分成两步:
  • 第一步 :先选择 Eureka Server ,它优先选择在同一个区域内负载较少的 Server。
  • 第二步 :再根据用户指定的策略,再从 Server 取到的服务注册列表中 选择一个地址

2. 使用

2.1 RestTemplate 的使用

  1. Ribbon 依赖

    新版的 Eureka 会自动把 Ribbon 也一起导入,所以不需要额外引入依赖

    在这里插入图片描述

  2. getForObject / getForEntity 的使用

  • getForObject 之前的 cloud-consumer-order-80 模块中就已经有用过了
    在这里插入图片描述

  • getForEntity ,这个方法返回的是一个对象

    @GetMapping("/getForEntity/{id}")
    public CommonResult<Payment> getPaymentById2(@PathVariable("id") Long id){
        ResponseEntity<CommonResult> entity = restTemplate.getForEntity(PAYMENT_URL + "/provider/getPaymentById/" + id, CommonResult.class);
    
        if (entity.getStatusCode().is2xxSuccessful()){
            //这个 实体对象 中还包含其他很多信息
            log.info("{状态码: " + entity.getStatusCode() +
                    ", 头信息: " + entity.getHeaders() + "}");
            // 如果成功
            return entity.getBody();
        }else {
            return new CommonResult<>(444, "【查询失败】");
        }
    }
    

    在这里插入图片描述
    在这里插入图片描述

  1. postForObject / postForEntity 的使用
  • postForObject ,之前也写过
    在这里插入图片描述

  • postForEntity

    @GetMapping("/postForEntity")
    public CommonResult<Payment> addPayment2(Payment payment){
        log.info("[=插入数据=]{"+ payment +"}");
        ResponseEntity<CommonResult> entity = restTemplate.postForEntity(PAYMENT_URL + "/provider/addPayment", payment, CommonResult.class);
        if (entity.getStatusCode().is2xxSuccessful()){
            //这个 实体对象 中还包含其他很多信息
            log.info("{状态码: " + entity.getStatusCode() +
                    ", 头信息: " + entity.getHeaders() + "}");
            // 如果成功
            return entity.getBody();
        }else {
            return new CommonResult<>(444, "【查询失败】");
        }
    }
    

    在这里插入图片描述
    在这里插入图片描述

2.2 负载均衡组件 - IRule

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

  2. 注意

    这个配置负载均衡的类 不能在主应用程序的上下文(也就是 主启动类的同级目录中),所以需要单独的创建一个包

  3. 新建一个包 myRule
    在这里插入图片描述

  4. 新建一个规则类 :MyRule

    @Configuration
    public class MySelfRule {
        @Bean
        public IRule myRule(){
            return new RandomRule();//定义为随机
        }
    }
    
  5. 主启动类添加 @RibbonClient

    @EnableEurekaClient
    @SpringBootApplication
    @RibbonClient(name = "CLOUD-PAYMENT-SERVICE",configuration = MySelfRule.class)
    public class OrderMain80 {
        public static void main(String[] args) {
            SpringApplication.run(OrderMain80.class, args);
        }
    }
    
  6. 测试

    启动两个 Provider ,一直发出查询请求,会在 8001、8002 随机选择

2.3 轮询原理

  • 原理(轮询)
    在这里插入图片描述
  • RoundRobinRule 源码分析
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

2.4 自定义一个负载均衡策略

  • 主要改造 cloud-consumer-order-80 模块
  1. ApplicationContextBean去掉@LoadBalanced
    在这里插入图片描述

  2. 在 8001、8002 的 Controller 中添加一个方法

    /** 用于测试自定义负载均衡 */
    @GetMapping(value = "/payment/lb")
    public String getPaymentLB(){
        return serverPort;
    }
    
  3. 新建一个 lb 的包,并在该包下建一个接口:LoadBalancer
    在这里插入图片描述

    public interface LoadBalancer {
        /** 收集服务器总共有多少台能够提供服务的机器,并放到list里面 */
        ServiceInstance instances(List<ServiceInstance> serviceInstances);
    }
    
  4. 为上面的接口写一个实现类

    @Component
    public class MyLB implements LoadBalancer {
    
        // 用于统计当前的访问次数
        private AtomicInteger atomicInteger = new AtomicInteger(0);
    
        /**
         * @return 当前是第几次访问
         */
        public final int getAndIncrement(){
            int current, next;
            // 自旋锁
            do {
                current = this.atomicInteger.get();
                next = current >= 2147483647 ? 0 : current + 1;
            }while (!this.atomicInteger.compareAndSet(current, next));
            System.out.println("【 当前访问次数 】 next = :"+next);
            return next;
        }
    
        @Override
        public ServiceInstance instances(List<ServiceInstance> serviceInstances) {
            // 通过取余,计算出当前应该是哪一个 Provider 提供服务
            int index = getAndIncrement() % serviceInstances.size();
            ServiceInstance instance = serviceInstances.get(index);
            System.out.println("当前是 【" + instance.getUri() + "】 提供服务");
            return instance;
        }
    }
    
  5. 修改 cloud-consumer-order-80 的 Controller

    /** 引入 自定义 的 LB */
    @Autowired
    private LoadBalancer loadBalancer;
    
    @Autowired
    private DiscoveryClient discoveryClient;
    
    /** 用于测试自定义负载均衡 */
    @GetMapping(value = "/payment/lb")
    public String getPaymentLB(){
        // 根据 ServiceId 获取 Server 集合
        List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
        // 如果没有服务
        if (instances == null || instances.size() <= 0){
            return null;
        }
        
        // 获取 Server 实例
        ServiceInstance serviceInstance = loadBalancer.instances(instances);
        // 通过实例获得该实例的 地址
        URI uri = serviceInstance.getUri();
        return restTemplate.getForObject(uri+"/provider/payment/lb",String.class);
    }
    
  6. 启动项目测试自己的轮询

    在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  • 控制台输出

    在这里插入图片描述

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yuan_404

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值