【Spring Cloud】Ribbon 实现负载均衡的原理,策略以及饥饿加载

news2024/11/25 20:45:33

文章目录

  • 前言
  • 一、什么是 Ribbon
  • 二、Ribbon 实现负载均衡的原理
    • 2.1 负载均衡的流程
    • 2.2 Ribbon 实现负载均衡的源码剖析
  • 三、Ribbon 负载均衡策略
    • 3.1 负载均衡策略
    • 3.2 演示 Ribbon 负载均衡策略的更改
  • 四、Ribbon 的饥饿加载
    • 4.1查看 Ribbon 的懒加载
    • 4.2 Ribbon 的饥饿加载模式


前言

在前文《深入理解 Eureka 注册中心的原理、服务的注册与发现》中,介绍了如何使用 Eureka 实现服务的注册与拉取,并且通过添加 @LoadBalanced 注解实现了负载均衡。这种自动化的背后隐藏着许多疑问:

  • 服务是在何时进行拉取的?
  • 负载均衡是如何实现的?
  • 负载均衡的原理和策略又是什么?

本文旨在深入探讨使用 Eureka 实现负载均衡的原理,为我们理解微服务架构中服务调用的内部机制提供更清晰的认识。通过解答这些疑惑,我们将更好地理解服务发现、负载均衡的运作方式,为构建高性能、稳定的分布式系统打下坚实的基础。

一、什么是 Ribbon

Ribbon 是一个基于 HTTP 和 TCP 客户端的负载均衡器。在微服务架构中,服务的调用通常涉及到负载均衡的问题,即在多个服务提供方中选择一个进行调用。Ribbon 提供了一种简单而有效的负载均衡解决方案。

Ribbon 最初是 Netflix 公司开发的,后来成为 Spring Cloud 项目的一部分。它的主要作用是在服务消费者和提供者之间实现均衡的流量分发,确保每个服务提供者都能够得到适当的请求,避免出现服务过载或资源浪费的情况。

具体而言,Ribbon 实现了以下功能:

  1. 负载均衡算法: Ribbon 支持多种负载均衡算法,例如轮询、随机、权重轮询等,使得服务消费者可以根据实际场景选择适当的负载均衡策略。

  2. 服务实例的自动发现: Ribbon 与 Eureka 等服务注册中心集成,能够自动获取可用的服务实例列表。

  3. 故障转移和重试机制: Ribbon 具备故障转移和重试功能,可以在服务提供者发生故障时自动切换到其他健康的实例,提高系统的稳定性和可用性。

在 Spring Cloud 中,Ribbon 作为一个负载均衡的客户端组件,通过拦截微服务的调用请求,动态地选择目标服务实例,从而分配请求的负载,实现了对服务调用的细粒度控制。

二、Ribbon 实现负载均衡的原理

2.1 负载均衡的流程

Ribbon 实现负载均衡的流程图如下:
负载均衡流程

下面是对这个流程的详细说明:

  1. 首先,服务消费者发起请求,Ribbon 负载均衡器收到请求之后,获取请求路径中的服务名称,例如 userservice
  2. 然后负载均衡器使用这个获取到的服务名称去向 Eureka Service 拉取对应的服务。
  3. 在实际生产中,一个服务一般都会有多个实例,因此拉取到的就是一个服务列表,列表中包含了这个服务所有正常实例的 IP 和端口号。
  4. 负载均衡器在获取到这个列表之后,使用当前采取的负载均衡策略去选择一个合适的服务,然后再访问这个服务。

这个流程确保了服务的请求能够被合理地分发到多个实例中,从而实现了负载均衡。

2.2 Ribbon 实现负载均衡的源码剖析

首先,Ribbon 实现负载均衡使用到的一个类叫做 LoadBalancerInterceptor负载均衡拦截器,可以通过 IDEA 查看它的源码:


发现它实现了一个 ClientHttpRequestInterceptor接口,即客户端 HTTP 请求拦截器:

它会拦截 RestTemplate 发生的 HTTP 请求,ClientHttpRequestInterceptor 是一个接口,并且其中包含了一个 intercept 方法,因此LoadBalancerInterceptor 作为实现这个接口的类也一定重写了 intercept 方法,此时我们可以在这个方法中设置一个断点进行调试,以追踪代码的运行:

  1. request.getURI() 获取请求地址:

  2. originalUri.getHost() 获取到了请求的地址中的主机名,此时获取到的就是服务的名称,也就是 userservice


3. 当找到了服务的名称之后,接下来要做的工作就是向 EurekaServer 去拉取对应的服务了,然后这个方法就把获取到的服务名交给了一个RibbonLoadBalancerClient (Ribbon负载均衡客户端)进行处理。

4. 继续调试代码,进入execute 方法:


  1. 继续往下走,就得到了一个 LoadBalancer 对象,:

    这个对象的名称叫做“动态服务列表均衡器”,查看这个对象的内容,可以发现服务列表中服务的数量为3,这三个服务就是获取到的三个 user-service 向 EurekaServer 中注册的服务。

因此getLoadBalancer 方法的作用就是根据服务名称向 EurekaServer 中寻找服务列表。当找到了服务列表之后,我们就可以大胆的猜测,下一步所要做的工作就是进行负载均衡操作了。

  1. 此时,我们进入 getServer 方法:

  2. 接下来就调用了chooseServer 方法,进入这个方法:

在这里插入图片描述

8. 然后再进入chooseServer 方法,最后找到了rule.choose方法:

此时查看 rule 对象,发现是一个接口:

既然是接口,那么就有实现类:

此时发现的实现类就是负载均衡的规则了。大致的规则有随机、轮询等等。

  1. 最后,通过默认的规则,就选择到了 8082 这个端口的服务了。


接下来就可以使用真正的IP和端口号去代替 userservice ,然后去访问指定了服务了。

以上就是 Ribbon 实现负载均衡的源码剖析,通过调试了方法深入探索了服务发现与负载均衡是实现流程,帮助我们更好的理解了服务发现、负载均衡的运作方式

三、Ribbon 负载均衡策略

3.1 负载均衡策略

通过上面的源码分析不难发现,Ribbon 的负载均衡规则是一个叫做 IRule 的接口来定义的,每一个子接口都是一种规则。

关于 IRule 接口的继承体系如下图所示:

关于 Ribbon 的负载均衡策略可以总结如下表所示:

内置负载均衡规则类规则描述
RoundRobinRule简单轮询服务列表来选择服务器。它是Ribbon默认的负载均衡规则。
AvailabilityFilteringRule对短路和并发数过高的服务器进行忽略
WeightedResponseTimeRule为每一个服务器赋予一个权重值。服务器响应时间越长,这个服务器的权重就越小。这个规则会随机选择服务器,这个权重值会影响服务器的选择。
ZoneAvoidanceRule以区域可用的服务器为基础进行服务器的选择。使用Zone对服务器进行分类,这个Zone可以理解为一个机房、一个机架等。而后再对Zone内的多个服务做轮询。
BestAvailableRule忽略那些短路的服务器,并选择并发数较低的服务器。
RandomRule随机选择一个可用的服务器
RetryRule重试机制的选择逻辑

Ribbon 提供了这些内置的负载均衡规则,同时也支持自定义负载均衡规则。在实际应用中,根据业务特点选择合适的负载均衡策略是非常重要的。下面演示了 Ribbon 负载均衡策略的更改。

3.2 演示 Ribbon 负载均衡策略的更改

通过定义IRule实现可以修改负载均衡规则,有两种方式:

  1. 代码方式:在order-service中的OrderApplication启动类中,定义一个新的IRule,并使用 @Bean 注解注册到 Spring 容器中:
@Bean
public IRule randomRule(){
    return new RandomRule();
}
  1. 配置文件方式:在order-serviceapplication.yml文件中,添加新的配置也可以修改规则:
# 修改 Ribbon 负载均衡策略
userservice:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 负载均衡规则

例如,下面是修改了负载均衡策略之后,再次使用 order-service 访问订单的结果。可以发现,现在不再是以轮询的方式挑选user-service服务了,而是以随机的方式进行挑选了。

四、Ribbon 的饥饿加载

4.1查看 Ribbon 的懒加载

当我们重新启动 order-service 服务,然后在浏览器中进行订单访问,可以发现如下的现象:

order-service 服务启动后,第一次访问服务可以发现耗时需要三百多毫秒:


然后,再次访问多次,可以发现耗时都变成了十几毫米:


通过以上的现象就可以发现,Ribbon 默认采用的是懒加载模式,就像单例模式的懒汉模式一样,第一次访问的时候才会去创建LoadBalanceClient实例,请求时间会很长。

4.2 Ribbon 的饥饿加载模式

为了解决上述懒加载的耗时问题,Ribbon 还提供了饥饿加载模式,饥饿加载则会在项目启动时创建,降低第一次访问的耗时。

通过下面配置开启饥饿加载:

此时重启 order-service服务:

在启动服务的时候,就会发现日志变得更多了:

这个日志的内容就是加载LoadBalanceClient实例所产生的日志。

再次首次访问 order-service服务,就会发现消耗的时间变短了:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1050510.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

《The Rise and Potential of Large Language Model Based Agents: A Survey》全文翻译

The Rise and Potential of Large Language Model Based Agents: A Surve - 基于 LLMs 的代理的兴起和潜力:一项调查 论文信息摘要1. 介绍2. 背景2.1 AI 代理的起源2.2 代理研究的技术趋势2.3 为什么大语言模型适合作为代理大脑的主要组件 3. 代理的诞生&#xff1a…

【文档智能】:GeoLayoutLM:一种用于视觉信息提取(VIE)的预训练模型

前言 文章介绍了一种用于视觉信息提取(VIE)的预训练模型:GeoLayoutLM。GeoLayoutLM通过显式建模几何关系和特殊的预训练任务来提高文本和布局的特征表示。该模型能够提高文档信息抽取的性能。 一、提出背景 当前多模态预训练模型在 SER 任…

c/c++中如何输入pi

标准的 C/C 语言中没有π这个符号及常量,一般在开发过程中是通过开发人员自己定义这个常量的,最常见的方式是使用宏定义: 方法1:#define pi 3.1415926 方法2:使用反三角函数const double pi acos(-1.0);

如何使用记事本制作一个简陋的小网页(2)——表格的建立

前情题要&#xff1a;http://t.csdnimg.cn/bK3H9 1.首先在之前的基础上建立一个新的标签页面。 2.使用<body></body>建立背景颜色 3.表格的填充和使用 如图所示&#xff0c;<table></table>是建立一个表格所需要使用的标签&#xff0c;可以使用width来…

Docker 容器跨主机通信 - Flannel

Author&#xff1a;rab 目录 前言一、架构及环境二、服务部署2.1 Etcd 部署2.2 Flannel 部署2.3 Docker 网络配置 三、容器通信验证及路由分析3.1 通信验证3.2 路由转发分析3.3 数据分发分析 总结 前言 今天是中秋佳节&#xff0c;首先在此祝大家“中秋快乐&#xff0c;阖家团…

java mongodb 并表 group 查询 Bson

对mongodb的使用中&#xff0c;需要将发生额表occr期初表open表&#xff0c;进行union的并表操作后&#xff0c;再进行group&#xff0c;sum&#xff0c;排序&#xff0c;分页操作。 查询了一番后&#xff0c;mongodb4.4版本后&#xff0c;新增了一个管道符&#xff0c;$union…

什么是 DNS 泛洪攻击(DNS 泛洪)

什么是 DNS 泛洪攻击&#xff08;DNS 泛洪&#xff09;&#xff1f; 域名系统 &#xff08;DNS&#xff09; 是用于在网站的机器可读地址&#xff08;例如 191.168.0.1&#xff1a;80&#xff09;和人类可读名称&#xff08;例如 radware.com&#xff09;之间进行解析的目录DN…

【HUAWEI】单臂路由

目录 ​ &#x1f96e;写在前面 &#x1f96e;2.1、拓扑图 &#x1f96e;2.2、操作思路 &#x1f96e;2.3、配置操作 &#x1f363;2.3.1、LSW4配置 &#x1f363;2.3.2、R2配置 &#x1f363;2.3.3、测试网络 &#x1f990;博客主页&#xff1a;大虾好吃吗的博客 &…

​面试官:谈谈 Go 泛型编程

大家好&#xff0c;我是木川 泛型编程是一种编程范式&#xff0c;它允许编写具有参数化类型的代码&#xff0c;从而增加代码的复用性和灵活性。在泛型编程中&#xff0c;你可以编写一段代码&#xff0c;使其适用于不同类型的参数&#xff0c;而不需要为每种类型编写不同的实现。…

js 时差计算 根据时间戳获取相差时间 几时几分几秒

上代码 // 如下&#xff1a;封装方法直接调用console.log("根据时间戳差值计算相差几小时几分几秒"); console.log("时间相差", timestampDifference(1695522383442 ,1695521518845)); 根据测试代码段 可以在浏览器控制台打印&#xff1a; 可以看到 &…

No148.精选前端面试题,享受每天的挑战和学习

🤍 前端开发工程师(主业)、技术博主(副业)、已过CET6 🍨 阿珊和她的猫_CSDN个人主页 🕠 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 🍚 蓝桥云课签约作者、已在蓝桥云课上架的前后端实战课程《Vue.js 和 Egg.js 开发企业级健康管理项目》、《带你从入…

No150.精选前端面试题,享受每天的挑战和学习

🤍 前端开发工程师(主业)、技术博主(副业)、已过CET6 🍨 阿珊和她的猫_CSDN个人主页 🕠 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 🍚 蓝桥云课签约作者、已在蓝桥云课上架的前后端实战课程《Vue.js 和 Egg.js 开发企业级健康管理项目》、《带你从入…

MacBook Pro 电池电量限制充电怎么设置AlDente Pro for Mac最大充电限制工具

通过充电电量限制工具可以更好的保护MacBook Pro的电池&#xff0c;通过 AlDente Pro 您可以设置电池的最大充电百分比设置为 20&#xff05; 至 100&#xff05;&#xff0c;然后&#xff0c;它将保持在所需的电池百分比&#xff0c;然后再次使用电源适配器进行充电。 AlDent…

led灯什么牌子的质量好?Led护眼台灯排行榜

现在我们很多家长对自己孩子的视力十分关心&#xff0c;生怕自己的孩子是近视、远视、弱视等等。对于父母而言&#xff0c;在孩子读书压力大课业重的关键时期&#xff0c;为孩子选择合适的桌椅&#xff0c;保护灯具从而保护孩子的眼睛是非常重要的事情!那么买给孩子读书做功课的…

如何改变讨好型人格,做回真正的自己

讨好型人格虽然说不上疾病那么严重&#xff0c;但是又切实的影响了正常的生活&#xff0c;阻碍了个人的事业发展&#xff0c;在人际关系方面也同样面临诸多不良影响。 根据我这段时间的阅读&#xff0c;尤其是知乎上关于讨好型人格的话题实在太多太多。那么如何才能摆脱讨好型…

Java实现使用多线程,实现复制文件到另一个目录,起不一样的名字,创建100万个数据

目录 1 需求2 实现 1 需求 我现在有一个300MB 的文件&#xff0c;想要根据这个文件&#xff0c;创建100万个大小一样的&#xff0c;名称不一样&#xff0c;如何实现&#xff0c;如何比较快点实现 2 实现 1 先准备好这个文件 2 准备好目录 3 写代码 private static void crea…

大数据分布式处理框架Hadoop

大数据是什么 大数据容量常以TB、PB、甚至EB为单位&#xff0c;远超传统数据库的承载能力&#xff0c;无论入库还是查询都出现性能瓶颈。 Hadoop是什么 Hadoop是开源的分布式计算技术框架&#xff0c;用于处理大规模数据和实现分布式存储。 Hadoop核心组件 HDFS&#xff08;…

PHP8的数据封装(数据隐藏)-PHP8知识详解

面向对象的特点之一就是封装性&#xff0c;也就是数据封装&#xff0c;也被称为数据隐藏。 php8通过限制访问权限来实现数据的封装性&#xff0c;这里用到了public、private、protected、static和final几个关键字。下面来介绍前3个。 1.、public&#xff08;公共成员&#xf…

JavaSE学习之--继承和多态

&#x1f495;"越是不思考的人&#xff0c;越不愿倾听别人说话。"&#x1f495; 作者&#xff1a;Mylvzi 文章主要内容&#xff1a;JavaSE学习之--继承和多态 目录 一.继承(inheritance) 1.为什么要有继承 2.继承的概念 3.继承的语法及代码实现 4.在子类中访…

【1++的Linux】之进程(四)

&#x1f44d;作者主页&#xff1a;进击的1 &#x1f929; 专栏链接&#xff1a;【1的Linux】 文章目录 一&#xff0c;进程创建二&#xff0c;进程等待三&#xff0c;进程终止 一&#xff0c;进程创建 在Linux中我们通过fork来创建一个新的进程。新创建的进程叫做子进程&…