SpringCloud笔记(九)-Ribbon

本文详细介绍了Spring Cloud Ribbon作为客户端负载均衡工具的原理和使用,包括其与Eureka的集成、负载均衡策略以及如何自定义负载均衡规则。通过实例展示了Ribbon的轮询和随机算法,并模拟实现了一个简单的轮询算法。最后,文章强调了Ribbon在微服务架构中的重要角色,帮助读者深入理解客户端负载均衡的概念。
摘要由CSDN通过智能技术生成

一、概述

1.1 什么是Ribbon

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

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

1.2 Ribbon 官网路径

https://github.com/Netflix/ribbon/wiki/Getting-Started

Ribbon 目前也进入了维护模式。

1.3 Ribbon 用途

1.3.1 负载均衡

        负载均衡(Load Balance) 就是将用户请求平均分配到多个服务上,从而达到系统的高可用(HA),常用的负载均衡软件有:Nginx、LVS、硬件F5等。

1.3.2 Ribbon 本地负载均衡客户端 和 Nginx 服务的负载均衡区别

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

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

        Nginx 的负载均衡是针对服务器层面的,比如多台服务器,而 Ribbon 的负载均衡是针对具体调用微服务层面的,比如微服务集群

1.3.3 集中式负载均衡 和 进程内的负载均衡

        集中式的负载均衡:即在服务的消费方和提供方之间使用独立的负载均衡设施,这些设施可以是硬件:比如 F5,也可以是软件:Nginx,由该设施负责把访问请求通过某种策略转发至服务的提供方。

        进程内的负载均衡:将负载均衡逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选择出一个合适的服务器。Ribbon 就属于此类负载均衡,它只是一个类库,集成于消费方进程,消费方通过它来获取到服务通过方的地址。

总的来说,Ribbon就是 负载均衡 +  RestTemplate 调用。

二、Ribbon负载均衡演示

2.1 Ribbon 负载均衡说明

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

Ribbon 在工作时分成两步:

1. 先选中 Eureka Server,它优先选择在同一个区域内负载较少的 Server;

2. 再根据用户指定的策略,在从 Server 取到的服务注册列表中选择一个地址。

其中 Ribbon 提供了多种策略:比如 轮询、随机和根据响应时间加权等

2.2  pom.xml 相关讲解

        从之前文章和例子中,我们可以得知,其实订单子模块(cloud-consumer-order80) 已经实现了负载均衡,但我们在 cloud-consumer-order80 子模块的pom.xml 中并未引入 Ribbon 的相关 maven 信息,那这边的负载均衡又是哪边来的呢?

        通过我们对 Eureka 引入的jar包分析可知,当前版本的 Eureka 客户端中已经集成了 Ribbon ,如下图:

从而我们可以不用在订单子模块中再次引入 Ribbon 也可以正常使用 Ribbon。 

2.3 RestTemplate 中 *ForEntity 相关用法

2.3.1 官网链接

https://docs.spring.io/spring-framework/docs/5.2.2.RELEASE/javadoc-api/org/springframework/web/client/RestTemplate.html

2.3.2 getForEntity 和 getForObject 区别

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

getForObject: 返回对象为响应体中数据转化成对象,基本可以理解为JSON对象;

2.3.3 demo 演示

执行结果:

三、Ribbon 核心组件 IRule

IRule:根据特地的算法,从服务列表中选取一个要访问的服务。

Ribbon 自带的常见负载均衡算法:

序号算法实现说明
1com.netflix.loadbalancer.RoundRobinRule轮询【默认算法】
2com.netflix.loadbalancer.RandomRule随机
3com.netflix.loadbalancer.RetryRule先按照 RoundRobinRule 的策略获取服务,如果获取服务失败,则在指定的时间内重试,获取可用的服务
4WeightedResponseTimeRule对 RoundRobinRule 的扩展,响应速度越快的实例选择权重就越大,越容易被选择
5BestAvailableRule会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务。
6AvailabilityFilteringRule先过滤掉故障实例,再选择并发较小的实例。
7ZoneAvoidanceRule复合判断 Server 所在区域的性能和 Server 的可用性选择服务器

四、替换Ribbon 默认的负载均衡规则

        Ribbon 的默认负载均衡规则是轮询算法,我们可以通过新建配置类修改负载均衡规则。因为 Ribbon 是客户端负载均衡工具,所以我们本次修改需要操作的是 order-consumer-order80 子模块。

1. 新建自定义负载均衡规则类 MySelfRule.java

         根据 Ribbon 官网信息,我们 Ribbon 的自定义配置类不能放在 @ComponentScan 所能扫描到的当前包及子包下,即 @SpringBootApplication 注解所在的包及子包下,所以我们这次新建的负载均衡自定义配置类 MySelfRule.java 放在 com.atguigu.ribbonselfrule 下。

package com.atguigu.ribbonselfrule;

import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @auther He
 * @date 2022-05-24 20:07
 */
@Configuration
public class MySelfRule {

    @Bean
    public IRule myCustomRule(){
        return new RandomRule();    //定义为随机算法
    }
}

特别注意:

        新建的包名和MySelfRule.java 中的方法名不能为 myrule,否则调用接口时会报错。

2. 修改启动类 OrderMain80.java

        修改order-consumer-order80 订单子模块的启动类 OrderMain80.java 增加 @RibbonClient 注解

package com.atguigu.springcloud;

import com.atguigu.ribbonselfrule.MySelfRule;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.ribbon.RibbonClient;

/**
 * @auther He
 * @date 2022-04-26 22:10
 */
@SpringBootApplication
@EnableEurekaClient
@RibbonClient(name = "CLOUD-PAYMENT-SERVICE", configuration = MySelfRule.class)
public class OrderMain80 {

    public static void main(String[] args) {
        SpringApplication.run(OrderMain80.class, args);
    }
}

3. 重启测试

        重启后,访问接口 http://localhost/consumer/payment/get/1 ,注意观察 返回内容中的端口号

 因为我们现在在当前接口中增加返回当前访问的端口信息,所以在返回信息中会显示对于的端口信息,通过观察可以发下,原来Ribbon 负载均衡算法为轮询时,返回的端口号为 8001和8002 交替出现,而经过我们上述修改,返回的端口号已经变成 8001 和 8002 随机返回了,也就代表是随便访问其中的一个端口。

五、负载均衡轮询算法原理 

六、模拟自定义轮询算法

        为加深对负载均衡算法的理解,跟着源码,模拟自定义实现轮询算法,仅为学习,非实际生产应用。

6.1 修改支付子模块中的controller

        分别修改支付子模块 (cloud-provider-payment8001 和 cloud-provider-payment8002) 中的 PaymentController.java,增加如下请求接口,作为修改完成后,校验是否修改成功的标记。

    /**
     * 获取当前端口
     * @return
     */
    @GetMapping("/payment/lb")
    public String getPaymentLB() {
        return serverPort;
    }

6.2 修改 订单模块启动类 OrderMain80.java

        注释原先增加的 @RibbonClient 注解,结果如下:

package com.atguigu.springcloud;

import com.atguigu.ribbonselfrule.MySelfRule;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.ribbon.RibbonClient;

/**
 * @auther He
 * @date 2022-04-26 22:10
 */
@SpringBootApplication
@EnableEurekaClient
//@RibbonClient(name = "CLOUD-PAYMENT-SERVICE", configuration = MySelfRule.class)
public class OrderMain80 {

    public static void main(String[] args) {
        SpringApplication.run(OrderMain80.class, args);
    }
}

6.3 修改 订单模块(cloud-consumer-order80)中RestTemplate 配置类 ApplicationContextBean.java

        修改 订单模块(cloud-consumer-order80)中RestTemplate 配置类 ApplicationContextBean.java,去除 @LoadBalanced 注解,让修改完后,订单模块中的负载均衡算法只能由我们自定义的轮询算法实现。

package com.atguigu.springcloud.config;

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

/**
 * @auther He
 * @date 2022-04-26 22:19
 */
@Component
public class ApplicationContextConfig {

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

6.4 新增我们自定义的负载均衡算法 LoadBalancer 接口

package com.atguigu.springcloud.lb;

import org.springframework.cloud.client.ServiceInstance;

/**
 * @auther He
 * @date 2022-05-26 22:37
 */
public interface LoadBalancer {

    /**
     * 获取 Eureka 中的一个服务对象
     * @return
     */
    public ServiceInstance getInstance();
}

6.5 增加上述自定义负载均衡接口的实现类 MyLB

package com.atguigu.springcloud.lb.impl;

import com.atguigu.springcloud.lb.LoadBalancer;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @auther He
 * @date 2022-05-26 22:41
 */
@Component
public class MyLB implements LoadBalancer {


    private AtomicInteger atomicInteger = new AtomicInteger(0);

    @Resource
    private DiscoveryClient discoveryClient;

    /**
     * 获取 Eureka 中的一个服务对象
     *
     * @return
     */
    @Override
    public ServiceInstance getInstance() {
        List<ServiceInstance> instanceList = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
        if (null == instanceList || instanceList.size() == 0) {
            return null;
        }
        //将获得的取模后获得返回服务的下标
        int index = this.getAndIncrement() % instanceList.size();
        return instanceList.get(index);  //根据返回的服务下标获得对应的服务实例
    }

    /**
     * CAS(自旋锁)保证原子性,获得下一个访问的请求数
     * @return
     */
    public final int getAndIncrement() {
        int current, next;
        do {
            current = this.atomicInteger.get(); //获取当前初始值
            next = current > Integer.MAX_VALUE ? 0 : current + 1;   //防止累加溢出
        } while (!this.atomicInteger.compareAndSet(current, next));

        System.out.println(">>> 第几次访问, 次数next: " + next);
        return next;
    }
}

核心在于:getAndIncrement() 方法,通过自旋锁保证操作原子性,并获得下一个访问的请求数。

6.5 修改 订单模块 中的 OrderController,增加测试自定义负载均衡的的请求接口


    /**
     * 测试自定义负载均衡算法
     * @return
     */
    @GetMapping(value = "/consumer/payment/lb")
    public String getPaymentLB() {
        ServiceInstance serviceInstance = loadBalancer.getInstance();
        if (null == serviceInstance) {
            return "未找到有效的服务地址";
        } else {
            URI url = serviceInstance.getUri();
            return restTemplate.getForObject(url + "/payment/lb", String.class);
        }
    }

6.6 重启服务,并测试

重启完后,循环访问 http://localhost/consumer/payment/lb ,若修改成功,会依次返回端口号 8001、8002

 

 

------------------------------------------------------  我是分割线 ------------------------------------------------------

        至此,Ribbon 的基础学习终于结束了,最近事情又杂又多,烦得很,没什么时间也静不下心来学习,只能尽量抽空逼自己坐下来学习,Ribbon 的学习时间大大超出了我先前的预期,希望后面能尽量避免这种情况发生。

        加油!

        让我们一起学习,一起成长,一起飞 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
很高兴能够为你提供关于Spring Cloud笔记Spring Cloud是一套基于Spring Boot的微服务框架,它提供了一系列开箱即用的工具和组件,帮助开发者快速构建和部署分布式系统。下面是一些关键的笔记要点: 1. 服务注册与发现:Spring Cloud提供了Eureka、Consul和Zookeeper等注册中心,用于服务的注册与发现,实现了服务之间的自动发现和负载均衡。 2. 服务调用:通过Spring Cloud的RestTemplate或Feign客户端,可以轻松实现服务之间的远程调用。Feign还支持声明式的服务调用,简化了代码的编写。 3. 负载均衡:Spring Cloud提供了Ribbon和LoadBalancer等组件,用于实现负载均衡。通过配置负载均衡策略,可以将请求分发到不同的服务实例上。 4. 服务容错:通过Hystrix或Sentinel等组件,可以实现服务的容错和熔断。当某个服务不可用时,可以快速失败或者返回默认值,保证整个系统的稳定性。 5. 配置中心:Spring Cloud Config提供了集中式的配置管理,可以将配置文件集中存储在Git、SVN等版本控制系统中,并实现配置文件的动态刷新。 6. 链路追踪:通过Spring Cloud Sleuth或Zipkin等工具,可以实现分布式系统的链路追踪和监控。可以了解每个请求经过的各个服务节点,便于排查和解决问题。 7. 消息驱动:Spring Cloud Stream和Spring Cloud Bus等组件,提供了消息驱动的方式来实现服务之间的解耦和异步通信。 这只是对Spring Cloud的一些简单介绍,希望能够帮助到你。如果有具体的问题或者需要进一步的资料,请随时提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值