负载均衡是高可用网络基础架构的关键组件,通常用于将工作负载分布到多个服务器来提高网站、应用、数据库或其他服务的性能和可靠性。
Spring Cloud Ribbon 是一个基于 HTTP 和 TCP 的客户端负载均衡工具,它基于 Netflix Ribbon 实现。通过 Spring Cloud 的封装,可以让我们轻松地将面向服务的 REST 模版请求自动转换成客户端负载均衡的服务调用。
Spring Cloud Ribbon 虽然只是一个工具类框架,它不像服务注册中心、配置中心、API 网关那样需要独立部署,但是它几乎存在于每一个 Spring Cloud 构建的微服务和基础设施中。因为微服务间的调用,API 网关的请求转发等内容,实际上都是通过 Ribbon 来实现的
一、使用
Ribbon在Spring Cloud 中的使用非常简单,因为我们的Nacos已经集成了Ribbon,这里我们就不需要再额外的引入jar包了。
只需要在RestTemplate的bean配置上加上注解@LoadBalanced
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
在我们使用RestTemplate做服务调用的时候就会自动进行负载均衡。
二、测试负载
- 启动两个provider,
2. 调用
从结果可以看到9001与9003都接受到了请求。负载成功。
三、Ribbon组件
接口 | 作用 | 默认值 |
---|---|---|
IClientConfig | 读取配置 | DefaultClientConfigIpml |
IRule | 负载均衡规则,选择实例 | ZoneAvoidanceRule |
Iping | 筛选掉ping不通的实例 | DummyPing |
ServerList | 交给Ribbion的实例列表 | Spring Cloud Alibaba : NacosServerList |
ServerListFilter | 过滤掉不符合条件的实例 | ZonePreferenceServerListFilter |
ILoadBalancer | Ribbon入口 | ZoneAwareLoadBalance |
ServerListUpdater | 更新交给Ribbon的List的策略 | PollingServerListUpdater |
四、Ribbon负载策略
上面提到IRule组件,我们找下该接口的具体实现
策略 | 描述 |
---|---|
AvailabilityFilteringRule | 过滤掉一直连接失败的被标记为circuit tripped(电路跳闸)的后端Service,并过滤掉那些高并发的后端Server或者使用一个AvailabilityPredicate来包含过滤Server的逻辑,其实就是检查status的记录的各个Server的运行状态 |
BestAvailableRule | 选择一个最小的并发请求的Server,逐个考察Server,如果Server被tripped了,则跳过 |
RandomRule 随机选择一个Server | |
ResponseTimeWeightedRule | 已废弃,作用同WeightedResponseTimeRule |
RetryRule | 对选定的负责均衡策略机上充值机制,在一个配置时间段内当选择Server不成功,则一直尝试使用subRule的方式选择一个可用的Server |
RoundRobinRule | 轮询选择,轮询index,选择index对应位置Server |
WeightedResponseTimeRule | 根据相应时间加权,相应时间越长,权重越小,被选中的可能性越低 |
ZoneAvoidanceRule | (默认是这个)负责判断Server所Zone的性能和Server的可用性选择Server,在没有Zone的环境下,类似于轮询(RoundRobinRule) |
五、更换不通负载策略
这里我们配置轮训策略
请求测试
这里我们可以看出,9001与9003轮流提供服务。
我们也可以自己实现IRule来自定义负载均衡策略。
六、Ribbon源码分析
6.1 为什么加上@LoadBalance之后才能把服务名转换为host:port
因为我们是通过@LoadBalanced注解来实现负载均衡的,那么首先看下注解@LoadBalanced的源码
从描述我们能看到,这个注解是让RestTemplate 去使用LoadBalancerClient
接着我们找下LoadBalancerClient的源码
LoadBalancerClient是spring cloud 定义的一个负载均衡器客户端接口,就是如果你要提供负载均衡支持,就必须要实现这个接口。
我们可以看到RibbonLoadBalancerClient实现了该接口。
接着往下看该接口定义的方法:
T execute(String serviceId, LoadBalancerRequest request) throws IOException
从负载均衡器中选择一个服务实例来执行请求。
URI reconstructURI(ServiceInstance instance, URI original);
这个方法就比较重要了,先看下描述
从描述我们看出,这个方法是把服务名称转换为host:port,这也就是为啥只要在使用了@LoadBalance,我们才能使用服务名称调用服务。解释了上一篇 服务调用最后抛出的结论
6.2 Ribbon默认负载策略是?
我们继续来看LoadBalancerClient接口,可以很容易的找到,只有Ribbon的RibbonLoadBalancerClient类实现了这个接口。
我们可以看到这个类的execute方法中,通过调用getServer来获取一个Server,接着往下跟
这个是重点,他是使用Ribbon自定义的一个ILoadBalancer接口来获取具体实例的,我们进入的这个接口
接口中定义了,增加服务,选择服务,通知服务下线,获取服务列表等方法
继续往下跟,找接口的实现类,
我们可以看到BaseloadBalancer类实现了基础的负载均衡,而DynamicServerListLoadBalancer和ZoneAwareLoadBalancer在负载均衡的策略上做了一些功能的扩展。
在BaseLoadBalancer类中定义了IRule
我们可以看到在BaseLoadBalance中默认定义的为随机策略,但是上面我们说了,Ribbon默认的策略是ZoneAvoidanceRule
接下来我们找一下RibbonClientConfiguration类,在这个类我们我们可以看到定义了一个IRule
这就是为啥默认的负载策略为ZoneAvoidanceRule。
接着我们看下IRule的实现类
从名字我们可以看出RandomRule表示随机策略、RoundRobinRule表示轮询策略、WeightedResponseTimeRule表示加权策略、BestAvailableRule表示请求数最少策略等等。
具体实现这里就不看了。
6.3 Ribbon中的拦截器是如何被加进去的
我们可以看到在LoadBalancerClient所在包的目录有一个类LoadBalancerAutoConfiguration,通过名字我们能看出这是LoadBalance的配置类。
通过注解我们可以发现,Ribbon实现负载均衡自动化配置,要求:
- RestTemplate必须在当前的工程环境中,
- 在Spring的Bean工程中必须有LoadBalancerClient的实现bean。
改配置类主要做了三件事:
- 创建了一个LoadBalancerInterceptor的Bean,用于实现对客户端发起请求时进行拦截,以实现客户端负载均衡。
- 创建了一个RestTemplateCustomizer的Bean,用于给RestTemplate增加LoadbalancerInterceptor,需要注意的是,这个bean上的注解@ConditionalOnMissingBean,如果已经有RestTemplateCustomizer在BeanFactory的时候,这个bean就不会被创建,这里打断点我们可以看到,这段代码并没有被执行,RestTemplateCustomizer是在RibbonAutoConfiguration这个类中被创建的;
- 维护了一个被@LoadBalanced注解修饰的RestTemplate对象列表,并在这里进行初始化,通过调用RestTemplateCustomizer的实例来给需要客户端负载均衡的RestTemplate增加LoadBalancerInterceptor拦截器。
Ribbon的源码还有很多没有跟,像RibbonClientConfiguration等等,在我们遇到具体问题的时候来扒一扒源码,基本都能找到答案。