第二章 负载均衡

  负载均衡是对系统的高可用、网络压力的缓解和处理能力扩容的重要手段之一。Spring Cloud Ribbon是一个基于 HTTP 和 TCP 的客户端负载均衡工具,它基于Netflix Ribbon实现,通过 Spring Cloud 的封装,可以让我们轻松地将面向服务的 REST 模板请求自动转换成客户端负载均衡的服务调用。Spring Cloud Ribbon几乎存在于每一个Spring Cloud 构建的微服务和基础设施中。因为微服务间的调用,API 网关的请求转发等内容实际上都是通过Ribbon来实现的

  通过Spring Cloud Ribbon的封装,我们在微服务架构中使用客户端负载均衡调用非常简单,只需要如下两步:

  -服务提供者只需要启动多个服务实例并注册到一个注册中心或是多个相关联的服务注册中心。
  -服务消费者直接通过调用被@LoadBalanced 注解修饰过的 RestTemplate 来实现面向服务的接口调用。

  这样,我们就可以将服务提供者的高可用以及服务消费者的负载均衡调用一起实现了。但是RestTemplate是Spring提供。跟Ribbon的客户端负载均衡又有什么关系,又是如何实现?下文详解。

一.LoadBalancerClent 和 RibbonLoadBalancerClient

  1.LoadBalancerClent 

  通过@LoadBalanced注解源码的注释中可以知道,该注解用来给RestTemplate做标记,以使用负载均衡的客户端(LoadBalancerClent)来配置它。下图贴出了LoadBalancerClent源码。

  

  1.1 Servicelnstance choose (String serviceid)根据传入的服务名serviceld, 从负载均衡器中挑选一个对应服务的实例。
  1.2 T execute (String serviceid, LoadBalancerRequest request)使用从负载均衡器中挑选出的服务实例来执行请求内容。
  1.3 URI reconstructURI(Serviceinstance instance , URI original )为系统构建 一个合适的host:port形式的URI。

  从方法名的诠释相信大家已经看出一些倪端,但是并不是大家想的那样(先调用choose挑选服务,再组成URI,最后调用execute执行请求)。因为本人这里这里踩过坑,先强调下!

  重要的是execute方法。

  2.RibbonLoadBalancerClient

  RibbonLoadBalancerClient是LoadBalancerClent的实现类。要点是它实现了父类的execute方法。贴出execute的方法实现。

  

  可以看出进入方法就调用自身的getServer方法获取服务,继续。

  

  IloadBalancer.chooseServer方法解释:通过某种策略,从负载均衡器中挑选出个具体的服务实例也就是这里去真正的去挑选我们调用的服务实例。但是IloadBalancer是一个接口,默认注入的是它的子类实现ZoneAwareLoadBalancer。也就是挑选服务的任务交给了ZoneAwareLoadBalancer。

  至于如何如挑选,在第四节负载均衡策略会详解。

  

二.LoadBalancerAutoConfiguration负载均衡地自动化配置类

  LoadBalancerAutoConfiguration为实现客户端负载均衡地自动化配置类。由于类源码稍长,就不贴了,我下图写出该类主要描述。

  

  1.被@ConditionalOnClass(RestTemplate.class)和@ConditionalOnBean(LoadBalancerClent.class)修饰表示在当前工程中必须有RestTemplate的Bean和LoadBalancerCliet的实现 Bean,理所当然嘛。

  2.创建了一个LoadBalancerInerceptor的Bean,用于实现对客户端发起请求时进行拦截, 以实现客户端负载均衡。这里贴出了LoadBalancerInerceptor的源码,我们可以看到在拦截器中注入了 LoadBalancerClient的实现,其实就是上文提到的RibbonLoadBalancerClient类!

   

  3.创建了一个RestTemplateCustomizer的Bean, 用于给RestTemplate增加LoadBalancerInterceptor拦截器。
  4.维护了一个被@LoadBalanced 注解修饰的RestTemplate对象列表,并在这里进行初始化,通过调用RestTemplateCustomizer的实例来给需要客户端负载均衡的RestTemplate增加LoadBalancerinInerceptor拦截器。

  我们暂且可以推出:当一个被@LoadBalanced注解修饰的RestTemplate对象向外发起HTTP请求时,会LoadBalancerinterceptor类的intercept函数所拦截,然后调用RibbonLoadBalancerClient类的execute函数。继续向下看!

三.ZoneAwareLoadBalancer负载均衡器

  

  我们已经知道ZoneAwareLoadBalancer是ILoadBalancer的实现类,并且ZoneAwareLoadBalancer.chooseServer方法是挑选出个具体的服务实例。但是ILoadBalancer和ZoneAwareLoadBalancer并不是直接实现关系,他们有多层实现继承关系,如下图。

  

  他们对ILoadBalancer一层层的实现扩展构成了ZoneAwareLoadBalancer,这里对他们具体如何实现扩展不做描述,大家有兴趣的可以去了解下!

  ZoneAwareLoadBalancer中要点:

  1.定义了负载均衡的处理规则IRule对象,负载均衡器实际将服务实例选择任务又委托给了IRule 实例中的choose函数来实现。而在这里,默认初始化了 RoundRobinRule为IRule的实现对象。RoundRobinRule 实现了最基本且常用的线性负载均衡规则(轮询),换而言之就是IRule的实现类就是定义不同的服务选择规则,比如轮询,随机,权重等等

  2.实现了服务实例清单在运行期的动态更新能力。同时它还具备了对服务实例清单的过滤功能:我们可以通过过滤器来选择性地获取一批服务实例清单。它定义了两个抽象方法:getInitialListOfServers用于获取初始化的服务实例清单, 而getUpdatedListOfServers用于获取更新的服务实例清单。

  在客户端负载均衡中,所有客户端节点都维护着自己要访问的服务端清单,而这些服务端的清单来自于服务注册中心,如上一章我们介绍的Eureka服务端。在客户端负载均衡中也需要心跳去维护服务端清单的健康性,只是这个步骤需要与服务注册中心配合完成。

  当Ribbon与Eureka联合使用时,Ribbon的服务实例清单RibbonServerList会被DiscoveryEnabledNIWSServerList重写,扩展成从Eureka注册中心中获取服务端列表。目的是为了对服务清单进行负载均衡需要的封装处理。利用Eureka的事件监听器来驱动服务列表的更新操作。它先创建了一个Runnable的线程实现,在该实现中调用了更新服务实例列表的方法, 最后再为这个Runnable线程实现启动了一个定时任务(默认30秒)来执行。

  简而言之ZoneAwareLoadBalancer的职责就是获取到服务清单,让IRole以某种规则选取!

  到这里我们就可以先整理下一个简单的负载均衡的流程:

  负载均衡流程:当一个被@LoadBalanced注解修饰的RestTemplate对象向外发起HTTP请求时,会被LoadBalancerinterceptor类的intercept函数所拦截。由于我们在使用RestTemplate时采用了服务名作为host,所以直接从 HttpRequest的URI对象中通过 getHost()就可以拿到服务名,然后调用RibbonLoadBalancerClient类的execute函数,函数中ZoneAwareLoadBalancer的 chooseServer函数通过某种规则获取了负载均衡策略分配到的服务实例对象Server(实质此任务委托给IRule的实现类RoundRobinRule<默认轮询>实例中的choose函数来实现),再将获取服务将其内容包装成RibbonServer对象,然后使用该对象再回调 LoadBalancerinterceptor请求拦截器中LoadBalancerRequest的apply(final ServiceInstance instance) 函数,向一个实际的具体服务实例发起请求,因为服务实例有ip,端口等元数据,从而实现一开始以服务名为host的URI请求到 host:post形式的实际访问地址的转换。

四.IRule负载均衡策略

  我们已经知道了IRule的实现类即是负载均衡规则,通过注入不同实现类来设定不同的选取策略。我们来看看它的一些常用实现类。

  1. RandomRule:该策略实现了从服务实例清单中随机选择一个服务实例的功能。具体是通过rand.nextint(serverCount)函数来获取一个最大为服务数量的随机Int变量,在服务列表通过索引值来返回具体实例。

  2. RoundRobinRule:该策略实现了按照线性轮询的方式依次选择每个服务实例的功能。循环条件内,为服务增加了一个count计数变量,该变量会在每次循环之后累加(线性轮询的实现则是通过Atomicinteger nextServerCyclicCounter对象实现,每次进行实例选择时通过调用 incrementAndGetModulo函数实现递增)。

  3. RetryRule:该策略实现了一个具备重试机制的实例选择功能。默认使用了 RoundRobinRule实例。而在choose方法中则实现了对内部定义的策略进行反复尝试的策略,若期间能够选择到具体的服务实例就返回,若一段时间(可设置)选择不到就根据设置的尝试结束返回 null。

  4. WeightedResponseTimeRule:该策略是对RoundRobinRule 的扩展,增加了根据实例的运行情况来计算权重,并根据权重来挑选实例,以达到更优的分配效果。会创建定时任务(默认30秒)用来为每个服务实例计算权重。权重机制其实是为每个服务生成权重区间,比如根据响应时间为三个服务生成了权重区间:实例A: [0, 100]   实例B: ( 100,150]   实例C: (150,300) (权重计算这里不做描述)。然后会生成一个[0, 最大权重值)区间内的随机数。查看随机数在那个区间,就拿到某个服务实例

  5. BestAvailableRule:该策略在实现中它注入了负载同时在具体的 choose 算法中利用保存的实例统计信息来选择满足要求的实例。通过遍历负载均衡器中维护的所有服务实例,会过滤掉故障的实例,并找出并发请求数最小的一个,所以该策略的特性是可选出最空闲的实例。

  6. PredicateBasedRule:这是一个基于 Predicate 实现的策略, Predicate是对集合进行过滤的条件接口。在choose函数中,先通过子类中实现的 Predicate 逻辑来过滤一部分服务实例,然后再以线性轮询的方式从过滤后的实例清单中选出一个。

  之前已经知道负载均衡流程,本节也介绍了常用的负载均衡策略,Spring Cloud负载均衡已经有了一定了解。本文也省略了一些内容比如过滤器工作机制,组装host:port的URI,如何封装负载均衡所需的服务清单等等,有机会的话我会单独介绍下!

  


 

本文链接:《Spring Cloud》学习(二) 负载均衡!
转载声明:本博客由静影残月创作。可自由转载、引用,但需署名作者且注明文章出处。

《Spring Cloud》学习(二) 负载均衡!的更多相关文章

  1. spring cloud: 关闭ribbon负载均衡

    spring cloud: 关闭ribbon负载均衡 1.eureka服务 2.2个user服务:7900/7901 3,movie服务 movie服务去请求 user的用户信息,而此时只想请求790 ...

  2. Spring Cloud 2-Ribbon 客户端负载均衡(二)

    Spring Cloud Eureka  1.Hello-Service服务端配置 pom.xml application.yml 启动两个service 2.Ribbon客户端配置 pom.xml ...

  3. SpringCloud学习笔记(8)----Spring Cloud Netflix之负载均衡-Ribbon的负载均衡的策略

    一. 内置 负载均衡策略的介绍的 IRule的实现类 2. 通过代码实现负载均衡 在第六节Riddom的使用的工程中,随机策略配置类 package com.wangx.cloud.springclo ...

  4. SpringCloud学习笔记(7)----Spring Cloud Netflix之负载均衡-Ribbon的深入理解

    1. 注解@LoadBalanced 作用:识别应用名称,并进行负载均衡. 2. 入口类:LoadBalancerAutoConfiguration 说明:类头上的注解可以知道Ribbon 实现的负载 ...

  5. SpringCloud学习笔记(6)----Spring Cloud Netflix之负载均衡-Ribbon的使用

    1. 什么是负责均衡? 负载均衡,就是分发请求流量到不同的服务器. 负载均衡一般分为两种 1. 服务器端负载均衡(nginx) 2. 客户端负载均衡(Ribbon) 2. 服务提供者(spring-c ...

  6. Spring Cloud学习 之 Spring Cloud Ribbon(负载均衡策略)

    文章目录 AbstractLoadBalancerRule: RandomRule: RoundRobinRule: RetryRule: WeightedResponseTimeRule: 定时任务 ...

  7. Spring Cloud Ribbon——客户端负载均衡

    一.负载均衡负载均衡(Load Balance): 建立在现有网络结构之上,它提供了一种廉价有效透明的方法扩展网络设备和服务器的带宽.增加吞吐量.加强网络数据处理能力.提高网络的灵活性和可用性.其意思 ...

  8. spring cloud 学习(二)关于 Eureka 的学习笔记

    关于 Eureka 的学习笔记 个人博客地址 : https://zggdczfr.cn/ ,欢迎光临~ 前言 Eureka是Netflix开发的服务发现组件,本身是一个基于REST的服务.Sprin ...

  9. spring cloud 之 客户端负载均衡 Ribbon

    一.负载均衡 负载均衡(Load Balance): 建立在现有网络结构之上,它提供了一种廉价有效透明的方法扩展网络设备和服务器的带宽.增加吞吐量.加强网络数据处理能力.提高网络的灵活性和可用性.其意 ...

  10. 【Spring Cloud】客户端负载均衡组件——Ribbon(三)

    一.负载均衡 负载均衡技术是提高系统可用性.缓解网络压力和处理能力扩容的重要手段之一. 负载均衡可以分为服务器负载均衡和客户端负载均衡,服务器负载均衡由服务器实现,客户端只需正常访问:客户端负载均衡技 ...

随机推荐

  1. 它们的定义Activity跳转动画

    本来觉得是一个非常小的需求, 后来我发现总是 错误, 采用Theme于 4.0在 操作不是很容易使用. 后来查阅资料, 须要在finish 后面 和 startActivity 后面加入 overri ...

  2. 批处理文件的工具(java+shell为了实现)

    批处理文件的工具(java+shell为了实现) 有一堆语料须要处理一下才干使用,本来应该能够直接用shell脚本直接处理的. 可是对shell脚本不熟,仅仅会简单的一些命令. 因此就利用java+s ...

  3. WPF 3D Transparency Depth-Order Sorting

    原文:WPF 3D Transparency Depth-Order Sorting   Just a quick post here - When making WPF 3D apps, trans ...

  4. 深入python3 (Dive Into Python 3) 在线阅读与下载

    在线阅读:http://book.doucube.com/diveintopython3/  中文版 下载地址:https://github.com/downloads/diveintomark/di ...

  5. Effective JavaScript Item 38 调用父类的构造函数在子类的构造函数

    作为这一系列Effective JavaScript的读书笔记. 在一个游戏或者图形模拟的应用中.都会有场景(Scene)这一概念.在一个场景中会包括一个对象集合,这些对象被称为角色(Actor). ...

  6. CentOS6.5优盘安装

    从CentOS6.5开始直接把iso文件写入u盘就可实现优盘安装 windows平台:1.用UltraISO打开iso(如:CentOS-6.5-x86_64-bin-DVD1.iso)2.然后点“启 ...

  7. HTTP协议入门(一)- 版本

    当我们在浏览器的地址栏输入URL后,信息会被发送到WEB服务器,服务器得到响应,将数据传输回来,展示到WEB页面上,这其中的传输方法就是HTTP协议. 一.HTTP 0.9 发布于1991年,是首个H ...

  8. 人像美妆---妆容迁移算法研究(Makeup transfer)

    原文:人像美妆---妆容迁移算法研究(Makeup transfer) 对于人像美妆算法,现在的美妆相机.玩美彩妆之类的app已经做的比较成熟了,但是具体算法,基本网络上是杳无可查,今天本人介绍一种自 ...

  9. GIS基础软件及操作(二)

    原文 GIS基础软件及操作(二) 练习二.管理地理空间数据库 1.利用ArcCatalog 管理地理空间数据库 2.在ArcMap中编辑属性数据 第1步 启动 ArcCatalog 打开一个地理数据库 ...

  10. Application.StartupPath和System.Environment.CurrentDirectory的区别

    System.Environment.CurrentDirectory的含义是获取或设置当前工作路径,而Application.StartupPath是获取程序启动路径,表面上看二者没什么区别,但实际 ...