天天看点

Spring Cloud教程 第二弹 客户端负载均衡Ribbon

更多Spring与微服务相关的教程请戳这里 Spring与微服务教程合集

1、浅谈负载均衡

负载均衡(Load Balance),是利用特定方式将流量分摊到多个操作单元上的一种手段。这个应该大家耳熟能详了!

负载均衡的分类:

硬负载:即利用硬件进行负载均衡处理,如F5

软负载:即利用软件进行负载均衡处理,如nginx

负载均衡的另一种分类:

集中式负载(服务端负载):集中式负载位于因特网与服务提供者之间,如nginx、F5

进程内负载(客户端负载):指从一个实例库(即服务注册中心)选取一个实例进行流量导入。这类负载的负载均衡器是类似与Ribbon的IPC(Inter-process communication,进程间通信 )组件

Spring Cloud教程 第二弹 客户端负载均衡Ribbon

2、走进Ribbo世界

简介:

Ribbon是一个负载均衡客户端,在实际使用中,Ribbon相当于一个服务消费者的存在。因此,我下面会介绍如何搭建以Ribbon组件为基础的消费者工程,Eureka Server工程与服务提供者工程这里暂不介绍

注意:

这里只有Ribbon入门实战中暂时不涉及ribbon相关的配置,ribbon相关配置后续介绍

RestTemplate是spring web模块提供的一个操作Rest接口的模板类,该类Ribbon似乎并没有任何关系

我们一直强调Ribbon是一个客户端负载均衡组件,正是@LoadBalanced注解让RestTemplate具有负载均衡能力,其背后的原理下面讲解

首先,服务提供者暴露一个返回自身端口号的接口,服务消费者消费该方法

关键代码:

restTemplate.getForObject("http://service-a/service-a/common/port", String.class);

本来URL应该是这样的:http://ip:port/service-a/common/port,但是由于Ribbon是从Eureka Server上拉取服务实例,因此这里可以将ip:port替换成服务名称。

而由于@LoadBalanced让RestTemplate具有负载均衡能力,所以当服务提供者有多个实例时(单机情况下,可以通过使用不同端口号来启动多个实例),每次返回的端口号也都不一样

首先看看Eureka Server的仪表盘

Spring Cloud教程 第二弹 客户端负载均衡Ribbon

这里我服务消费者启动了一个实例,而服务提供者启动了多个实例,浏览器多次访问​​http://localhost:8020/consumer-a/common/provider/port​​  可以看到页面显示的端口号是变化的,这说明负载均衡生效

Spring Cloud教程 第二弹 客户端负载均衡Ribbon
Spring Cloud教程 第二弹 客户端负载均衡Ribbon
Spring Cloud教程 第二弹 客户端负载均衡Ribbon

Ribbon核心接口共同定义了Ribbon的行为,是Ribbon的骨架。官方文档上指出了Ribbon的核心接口,下表显示了Spring Cloud Netflix默认为Ribbon提供的bean

<col>

Bean Type

Bean Name

 Class Name 

描述

IClientConfig  

ribbonClientConfig  

DefaultClientConfigImpl  

定义配置

IRule  

ribbonRule  

ZoneAvoidanceRule  

定义负载均衡策略

IPing  

ribbonPing  

DummyPing  

定期Ping服务检查可用性

ServerList

ribbonServerList

ConfigurationBasedServerList

定义服务列表

ServerListFilter

 ribbonServerListFilter

ZonePreferenceServerListFilter  

定义特定期望获取服务列表

ILoadBalancer   

ribbonLoadBalancer

ZoneAwareLoadBalancer  

定义负载均衡选择服务的核心方法

ServerListUpdater

 ribbonServerListUpdater

 PollingServerListUpdater

为DynamicServerListLoadBalancer定义动态更新服务列表接口

由于Ribbon的负载均衡策略是由IRule接口定义的,因此下面看一下IRule接口的继承关系:

绿色表示接口,蓝色表示抽象类

IRule

        AbstractLoadBalancerRule (com.netflix.loadbalancer)

                ClientConfigEnabledRoundRobinRule (com.netflix.loadbalancer) 这个类看名字就知道实际上是用的RoundRobinRule

                        BestAvailableRule (com.netflix.loadbalancer) (1)最低并发策略

                        PredicateBasedRule (com.netflix.loadbalancer)

                                ZoneAvoidanceRule (com.netflix.loadbalancer) (2)区域回避策略

                                AvailabilityFilteringRule (com.netflix.loadbalancer) (3)可用过滤策略。过滤掉一直连接失败和存在高并发连接的server

                RoundRobinRule (com.netflix.loadbalancer) (4)轮询策略

                        WeightedResponseTimeRule (com.netflix.loadbalancer) (5)响应时间加权策略。根据server的响应时间分配权重,响应时间越长,权重越低。综合了网络磁盘IO各种因素,这些因素直接影响了响应时间

                        ResponseTimeWeightedRule (com.netflix.loadbalancer) 过时,不建议使用

                RandomRule (com.netflix.loadbalancer) (6)随机策略

                RetryRule (com.netflix.loadbalancer)  (7)重试策略

综上所属,Ribbon默认有7种负载均衡策略

IRule接口源码:

choose方法主要实现负载均衡逻辑

在调用过程中,Ribbon是通过ILoadBalancer来关联IRule的,ILoadBalancer的chooseServer方法会转换为调用IRule的choose方法。

AbstractLoadBalancerRule类将IRule接口与ILoadBalancer接口关联起来了

AbstractLoadBalancerRule类源码:

关于Ribbon核心工作原理的源码解读,请看这篇文章 Spring Cloud教程 第三弹 Ribbon工作原理

3、个性化配置

对于Ribbon的核心接口,都是按默认规则配置好的,比如负载均衡算法,默认采用的是ZoneAvoidanceRule 算法,如果我们想采用别的负载均衡算法呢,那么需要我们做一些个性化配置

只需要将个性化配置类打上@Configuration注解,并且让spring boot扫到它即可生效

首先要做的事就是,避免让spring boot扫到CustomRibbonConfig ,通过@ComponentScan注解的excludeFilters属性实现

再通过@RibbonClients注解的defaultConfiguration属性实现全局配置

首先自定义一个注解

将@AVoidScan注解打在CustomRibbonConfig类上,然后再spring boot的启动类上分别打上@ComponentScan注解和@RibbonClients注解,部分代码如下所示

同样要做的事就是,避免让spring boot扫到CustomRibbonConfig,实现方式上面已经讲过了

再通过@RibbonClient注解针对单个服务源进行配置,如下所示

格式为:      

*代表配置项的key,key在CommonClientConfigKey类中定义,而值在RibbonProperties中获取

比如我要配置负载均衡策略,配置细节如下:

当多种配置方式并存时,其优先级如下:

配置文件中定义的 &gt; RibbonClient注解定义的 &gt; 默认的

4、使用负载均衡API

除了通过RestTemplate进行负载均衡,还可以直接使用负载均衡客户端

5、Ribbon缓存机制

Ribbon并不是实时从eureka server上更新服务列表的,而是有一个缓冲时间,默认每30秒从eureka server上更新一次服务实例,可以通过如下配置项修改

6、不使用Eureka

ribbon默认是结合eureka使用的,ribbon会定期从eureka server上拉取实例列表

ribbon同样支持不使用eureka,如何实现呢?

首先要做的是去除eureka client的依赖

去掉@EnableEurekaClient注解

关闭eureka

在配置文件中针对单个服务源手动配置实例列表

7、开启Ribbon的饥饿加载

Ribbon在进行客户端负载均衡时并不是启动时就加载上下文,而采用了懒加载,即第一次请求的时候才会去加载,这样会导致第一次请求发生超时

初始化方法是RibbonApplicationContextInitializer.initialize方法

如何开启饥饿加载?如下所示

8、Ribbon的超时与重试机制

对于重试机制:如果ribbon在重试期间,时间超过了hystrix的超时时间,则会熔断。因此ribbon的重试时间 必须要小于 hystrix超时时间,否则重试没有意义

对于OkToRetryOnAllOperations,false表示只会对get请求重试。而true表示所有请求都重试。true要慎用,如果接口没有做幂等性,重试可能会导致不良的后果

默认情况下,GET方式请求无论是连接异常还是读取异常,都会进行重试,非GET方式请求,只有连接异常时,才会进行重试

继续阅读