一、Spring Cloud Ribbon 客户端负载均衡
Ribbon是Netflix发布的云中间层服务开源项目,其主要功能是提供客户端侧负载均衡算法。Ribbon客户端组件提供一系列完善的配置项如连接超时,重试等。Bibbon 的架构图如下所示:
简单的说,Springcloud Ribbon 是一个基于HTTP和TCP的客户端负载均衡工具。Springcloud 将面向服务的REST 模板请求自动转换成客户端负载均衡的服务调用 。我们可以在配置文件中列出Load Balancer后面所有的机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随机连接等)去连接这些机器,我们也很容易使用Ribbon实现自定义的负载均衡算法。
客户端负载均衡是对系统的高可用、网络压力的缓解和处理能力扩容的重要手段之一。通常意思上的负载均衡是指服务端负载均衡其中分为硬件负载均衡和软件负载均衡。硬件和软件负载基本原理图如下所示:
客户端负载均衡中所有客户端节点都要维护着自己要访问的服务端清单,这些服务端的清单来自于服务注册中心(Eureka)。
二、实现客户端负载均衡
服务提供者(Service Provider)只需要启动多个服务实例并注册到一个注册中心或是多个相关联的服务注册中心。
服务消费者(Service consumer)直接通过调用被@LoadBalanced 注解修饰过的TestTemplate 来实现面服务的接口调用。
三、Eureka使用Ribbon架构
Ribbon工作时分为两步:
第一步先选择 Eureka Server,获取可用服务列表,从可用的服务列表中它优先选择在同一个Zone且负载较少的Server;
第二步再根据用户指定的策略,在从Server取到的服务注册列表中选择一个地址。其中Ribbon提供了多种策略,例如轮询round robin、随机Random、根据响应时间加权等。
四、ribbon的9种负载均衡策略
ribbon支持的负载均衡策略主要有九种:1、线性轮询策略;2、重试策略;3、加权响应时间策略;4、随机策略;5、客户端配置启动线性轮询策略;6、最空闲策略;7、过滤性线性轮询策略;8、区域感知轮询策略;9、可用性过滤策略。
以下是一个简单的使用Ribbon定义负载均衡策略的例子:
@Configuration
public class RibbonConfig {
@Bean
public IRule ribbonRule() {
// 这里可以选择你需要的策略,例如轮询策略
return new RoundRobinRule();
}
}
在这个配置类中,我们定义了一个名为ribbonRule的Bean,
这个Bean将被用作Ribbon的负载均衡策略。
在这个例子中,我们使用了简单的轮询策略,
但你可以根据需要更改为其他策略。
1、线性轮询策略: RoundRibbonRule
BaseLoadBalancer 负载均衡器默认采用线性负载轮询负载均衡策略。
工作流程:
RoundRibbonRule 类的 choose(ILoadBalancer Ib,Object key) 方法初始化一个计数器。
incrementAndGetModulo() 方法获取一个下标 (是先加1,然后和服务清单总数取模获取到的,不会越界),是一个不断增长的数。
chooseServer(Object key) 方法拿着下标去服务列表中获取服务,每次循环计数器都会加1。如果连续10次都没有取到服务,则会报 “No available alive servers after 10 tries from loadbalancer.” 警告。
2、重试策略:RetryRule
重试策略在使用 RetryRule 类中定义的 choose(ILoadBalance Ib,Object key) 方法来选择一个服务实例。
choose() 方法也是采用 RoundRibbonRule 中的 choose() 方法来选择一个服务实例的。
工作流程
如果选择到的服务实例正常,则返回数据。
如果选择到的服务实例为 null 或 失效,则 choose() 方法会在失效时间前不断地进行重试。
如果超过了失效时间还是没有取到,则返回一个 null。
3、加权响应时间策略:WeightedResponseTimeRule
WeightedResponseTimeRule 类是 RoundRibbonRule 的一个子类,它对 RoundRibbonRule 的功能进行了扩展。它根据每一个服务实例的运行情况先计算出该服务实例的一个权重,然后根据权重进行服务实例的挑选,这样能够调用到更优的服务实例。
工作流程
每 30S 计算一次各服务实例的响应时间,以响应时间来计算权重。平均响应时间越短则权重越高,权重越高则被选中的的概率越高,反之则被选中的概率较低。
WeightedResponseTimeRule 中有一个名叫 DynamicServerWeightTask 的定时任务。它是一个后台线程,定期从 status 里面读取响应时间,用来计算每个服务实例权重。
4、随机策略:RandomRule
随机选择一个可用的服务实例。
工作流程
负载均衡通过 upList() 和 allList() 方法获得可用服务实例列表,然后初始化了一个 Randow 对象以生成一个不大于服务实例总的随机数。
choose() 方法将该随机数作为下标获取一个服务实例。轮询 “index” 选择 “index” 对应的服务实例。
5、客户端配置启动线性轮询策略:ClientConfigEnabledRoundRobbinRule
继承该策略默认的 choose() 方法就能实现线性轮询机制。
6、最空闲策略:BestAvailableRule
该服务是逐个考察各服务实例,然后选择一个最小的并发请求的服务实例来提供实例。BestAvailableRule 继承自 ClientConfigEnabledRoundRobbinRule 类。
工作流程
根据在 loadBalancerStats() 方法中保存的服务实例的状态信息来过滤失效的服务实例。
判断 loadBalancerStats 是否为空。
如果 loadBalancerStats 不为空,则找出并发请求最小的服务实例来使用。
如果 loadBalancerStats 为空,则 BestAvailableRule 类将采用它的父类。即 ClientConfigEnabledRoundRobbinRule 的服务选取策略 (线性策略)。
7、过滤性线性轮询策略:PredicateBasedRule
PredicateBaseRule 类是 ClientConfigEnabledRoundRobbinRule 类的一个子类,它通过内部定义的一个过滤器过滤出一部分服务实例清单,然后用线性轮形的方式从过滤出来的服务实例清单中选取提个服务实例。
8、区域感知轮询策略:ZoneAvoidanceRule
该策略以区域、可用的服务器为基础,选择服务实例并对服务实例进行分类。ZoneAvoidanceRule 类是 PredicateBasedRule 类的一个实现类,它有一个组合过滤条件 (CompositePredicate)。ZoneAvoidanceRule 类中的过滤条件是 “以 ZoneAvoidancePredicate() 方法为主过滤条件” 和 “以AvailabilityPredicate() 方法为次过滤条件” 组成的。在过滤成功后,继续采用线性轮询的方式从过滤结果中选择出一个服务实例。
9、可用性过滤策略:AvailabilityFilteringRule
该策略根据服务状态 (宕机和繁忙) 来分配权重,过滤掉那些因为一直连接失败或高并发的服务实例。它使用一个 AvailabilityPredicate() 方法来包含过滤逻辑。
五、Ribbon自带负载均衡策略比较
六、Ribbon实战
一、如何添加并整合Ribbon
这段英文中主要提到添加整合Ribbon,需要引入spring-cloud-starter-ribbon 这个资源,但默认spring-cloud-starter-erueka的资源包已经包含了对于ribbon的实现。所以在pom.xml 配置中就不需要引入这个资源路径了。前面咱们已经介绍过erueka的引入和实现。
二、怎么使用Ribbon呢?使用Ribbon非常简单,不信往下看。
如何使用ribbon呢,主要是在启动类中添加@LoadBalanced注解。
在RestTemplate 上面添加@LoadBalanced这个注解,就可以实现使用ribbon;服务消费者直接通过调用被@LoadBalanced 注解修饰过的RestTemplate 来实现面向服务的接口调用 。RestTemplate,会使用Ribbon的自动化配置,通过@LoadBalanced开启客户端负载均衡。
GET请求 :getForEntity函数,它是对HTTP请求响应的封闭,其中主要存储了HTTP的几个重要元素。其它还包含:POST请求、PUT请求、DELETE请求。
三、Ribbon使用的策略
使用的是默认的策略,默认是轮询策略
四、@LoadBalancerClient
在Riibon中一个重要的组件为LoadBalancerClient,它作为负载均衡的一个客户端。它在spring-cloud-commons包下的LoadBalancerClient是一个接口,它的实现类是RibbonLoadBalancerClient 。最终的负载均衡的请求处理,由它来执行。
LoadBalancerClient是在初始化的时候,会向Eureka回去服务注册列表,并且向通过10s一次向EurekaClient发送“ping”,来判断服务的可用性,如果服务的可用性发生了改变或者服务数量和之前的不一致,则更新或者重新拉取。LoadBalancerClient有了这些服务注册列表,就可以根据具体的IRule来进行负载均衡。
五、为什么在RestTemplate加一个@LoadBalance注解就可以开启负载均衡呢?
LoadBalancerAutoConfiguration类,即LoadBalancer自动配置类。在该类中,首先维护了一个被@LoadBalanced修饰的RestTemplate对象的List,在初始化的过程中,通过调用customizer.customize(restTemplate)方法来RestTemplate增加拦截器LoadBalancerInterceptor。而LoadBalancerInterceptor,用于实时拦截,在LoadBalancerInterceptor这里实现来负载均衡。
六、总结
Ribbon的负载均衡,主要通过LoadBalancerClient来实现的,而LoadBalancerClient具体交给了ILoadBalancer来处理,ILoadBalancer通过配置IRule、IPing等信息,并向EurekaClient获取注册列表的信息,并默认10秒一次向EurekaClient发送“ping”,进而检查是否更新服务列表,最后,得到注册列表后,ILoadBalancer根据IRule的策略进行负载均衡。
而RestTemplate 被@LoadBalance注解后,能过用负载均衡,主要是维护了一个被@LoadBalance注解的RestTemplate列表,并给列表中的RestTemplate添加拦截器,进而交给负载均衡器去处理。
Ribbon常见问题和补充说明
一、 Ribbon-使用配置文件自定义Ribbon Client
从1.2.0开始,Spring Cloud Netflix支持使用properties(配置文件)来定制化Ribbon clients。所以这就可以在不同的服务环境中在启动的时候就可以改变,实现负载客户端。
1、说明
属性文件配置中,支持的属性如下(name.ribbon.属性):
- NFLoadBalancerClassName: 实现ILoadBalancer
- NFLoadBalancerRuleClassName: 实现IRule
- NFLoadBalancerPingClassName: 实现IPing
- NIWSServerListClassName: 实现ServerList
- NIWSServerListFilterClassName: 实现ServerListFilter
2、举个例子
例如一个叫microservice-provider-user(服务提供者)的服务设置IRule,如下图所示:
3、注意事例
请注意加载时的优先级。在这些属性中定义的Classes优先于@RibbonClient(configuration=MyRibbonConfig.class)定义的beans和由Spring Cloud Netflix提供的缺省值。
4、注意事例:spring.application.name同名导致负载均衡策略并未生效
- 新建三个spring.application.name同名的生产者微服务
- 新建一个消费者微服务添加Ribbon依赖
- 依次启动服务
执行结果:Ribbon负载均衡策略并未生效;
原因:三个生产者服务自定义服务名称信息相同,application.yml配置eureka.instance.instance-id修改成不同的名称,问题解决;
二、Using Ribbon with Eureka 如果实现Ribbon与Eureka 联合使用
下面是我从官方文档文明中截取的一张图,里面说的比较详细。
下面翻译的内容也有点不通顺,大家先理解一下。我觉得知道概念就可以了,没必要去光太多时间深入研究。在实际应用的时候用到了再来理解和学习这样比较好。
当Eureka被和Ribbon一起联合使用时, ribbonServerList被一个叫做DiscoveryEnabledNIWSServerList的扩展重载。这个扩展汇聚了来自于Eureka的服务列表。
IPing的接口也被NIWSDiscoveryPing代替,用于委托Eureka来测定服务是否UP。 缺省的ServerListThe是一个DomainExtractingServerList,目的是使得物理上的元数据可以用于负载均衡器,而不必要使用AWA AMI的元数据。 缺省情况下,服务List将根据“zone”信息被创建。(远程客户端设置eureka.instance.metadataMap.zone)。如果这个没有设置,它可以使用d来自于服务hostname的domain name作为zone的代理(取决于approximateZoneFromHostname是否被设置)。一旦那个zone的信息可获得,他可以在ServerListFilter被使用。缺省情况下,由于使用ZonePreferenceServerListFilter,它被用于定位一个和Client相同zone的server。 默认情况下,client的zone是和远程实例一样的,通过eureka.instance.metadataMap.zone来设置。
三、How to use Ribbon Without Eureka 脱离Eureka怎么使用用Ribbon
下面是我从官方文档文明中截取的一张图,里面说的比较详细。
上面这段英文翻译过来的主要意思是:
Eureka是一个实用的方式,它抽象了远程服务的发现机制,可以使你不用在Clinet中硬编码URLS。但如果你不打算使用它,Ribbon和Feign仍然是相当可用的。假设你已经声明了一个@RibbonClient为一个“Stores”的服务。Eureka没有被使用。Ribbon Client会缺省到一个已配置的Server列表。你可以提供配置像: stores.ribbon.listOfServers: example.com,google.com
Example: 没有Eureka,如何使用Ribbon
ribbon.eureka.enabled=false
四、Using the Ribbon API Directly 使用Ribbon API
下面这段代码中直接使用LoadBalancerClient
五、 重试机制
由于Eureka 实现服务治理机制中,强调AP(可用性与可靠性),从而牺牲了一定的一致性。在极端情况下它宁愿接受故障的实例也不要丢掉“健康”实例。比如,当服务注册中心的网络发生故障断开时,由于所有的服务实现无法维持续约心跳,在强调AP的服务治理中心将会把所有服务实例剔除掉,而EUREKA会因为超过85%的实例丢失心跳而会触发保护机制,注册中心会保留此时的所有节点,以实现服务间依然可以进行互相调用的场景,即使其中有部分故障节点,但这样可以继续保障大多数的服务正常消费。
通过RestTemplate 实现的服务访问就会自动根据配置来实现重试策略。
如下是配置详细:
六、总结
Ribbon是一个客户端负载均衡器,能够给HTTP和TCP客户端带来灵活的控制。Ribbon的核心概念是命名的客户端。每一个负载均衡器都是一系列工作在一起的组件的一部分,并用于按需联系远程服务。