负载均衡:Ribbon源码剖析(1)创建负载均衡器
入口
我们在每次使用Spring Cloud Netflix Ribbon的时候,有一个固定的套路就是需要用@LoadBalanced
注解修饰一个RestTemplate
1 |
|
根据以往Spring Cloud集成套路,必然会有一个与之相关的自动配置类,果不其然,找到了一个LoadBalancerAutoConfiguration
1 | false) (proxyBeanMethods = |
- ConditionalOnBean
会依赖一个叫LoadBalancerClient
的对象 - LoadBalancerRetryProperties
开启了一个配置,是Ribbon重试相关的
下面这行代码,比较灵活的运用了Spring原生注解的特性,作用就是把所有的被@LoadBalanced注解修饰的RestTemplate对象都收集到这个集合里。
1 | class RibbonAutoConfiguration |
其实现原理,玄机在
@LoadBalanced
中,可以看到该注解上被@Qulifier
注解修饰,这就触发了Spring一个比较偏门的特性,Spring会把这个注解修饰的对象注册到Spring容器中去。
1
2
3
4
5
6
7
8
9 > ({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
> (RetentionPolicy.RUNTIME)
>
>
> // 注意这个
> public LoadBalanced {
>
> }
>
有兴趣的朋友可以看看Spring处理
@Qulifier
的过程。QualifierAnnotationAutowireCandidateResolver#checkQualifier方法
接着,我们看到了Spring集成Ribbon关键的一步
1 | class RibbonAutoConfiguration |
LoadBalancerInterceptor
这是Spring与Ribbon代码的关键结合点。他是通过Spring为Ribbon定制的RestTemplate拦截器来实现的,在这个拦截器里,完成了将RestTemplate收到的请求委托给Ribbon处理的逻辑。restTemplateCustomizer
这里就是对Ribbon的RestTemplate拦截器加入到restTemplate中去。
这一步的逻辑要结合着此处去看,一下子就明白了。原来Spring就是把被@LoadBalanced
修饰的RestTemplate收集起来,然后一个个遍历,在每一个RestTemplate里追加一个为Ribbon定制的拦截器,完成了请求的委托处理,这算是Spring整合Ribbon最核心的逻辑之一了。1
2
3
4
5
6
7
8
9
10
11
12
13class RibbonAutoConfiguration
@Bean
public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(
final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {
return () -> restTemplateCustomizers.ifAvailable(customizers -> {
for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
for (RestTemplateCustomizer customizer : customizers) {
customizer.customize(restTemplate);
}
}
});
}
Spring整合Ribbon的核心结合点
既然如此,我们就看看在为Ribbon定制的RestTemplate拦截器里具体完成了什么事情吧
1 | public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor { |
- LoadBalancerClient
这个里面其实存放了Ribbon的几个核心组件,比如ILoadBalancer、IRule、RestClient、IPing等等,这个我们后面再仔细看,这里有个印象就好 - LoadBalancerRequestFactory
这里完成了遍历RestTemplate里的拦截器链,并一个个执行 - loadBalancer.execute
这里最最关键,完成了RestTemplate请求到Ribbon请求的转换,并且将请求委托给Ribbon处理
这段代码实现的非常优雅,不像我们之前分析Eureka源码的时候,都是大段大段的代码,看起来令人眼花缭乱,其最主要的槽点就是控制和业务逻辑没有做到很好的分离。人家Spring通过一系列组件的封装,控制逻辑,比如这里的请求转换,请求委托都清爽的呈现在我们眼前,让人一眼就知道Spring干了什么。如果想更详细的了解具体的处理逻辑,只要看那个组件的很少的代码就可以了,非常的内聚,我们要向这种代码风格学习
看看到底负载均衡器是如何创建的
我们上面已经分析了Spring集成Ribbon的核心骨架代码的一部分了,剩下的其实都是比较零散,也可以说非常内聚的点了,逐个击破即可。
其实在RibbonAutoConfiguration
中还有一个关键的对象被创建了
1 | class RibbonAutoConfiguration |
我们看到他的方法列表,就会倍感熟悉,这ILoadBalancer不就是负载均衡器吗?
1 | class SpringClientFactory |
我们此时先按捺住激动的心情,先看看构造器里的RibbonClientConfiguration
是个什么东东吧。
根据笔者阅读Spring集成代码的经验,涉及到三方技术核心组件创建的这样重要逻辑,Spring往往是在xxConfiguration里完成的。
1 | class RibbonClientConfiguration |
- 我们的选择是没有白费,看看这是什么,这里构建了一个IRule的实现ZoneAvoidanceRule,Ribbon组件+1
1 | class RibbonClientConfiguration |
ServerList
服务列表,Ribbon的核心概念ServerListUpdater
服务列表更新器,我们知道Ribbon的服务列表肯定是来自于Eureka,这个更新器的作用就不难猜测了
PollingServerListUpdater,定时从Eureka那边拉取服务列表,至此,Spring集成Ribbon的所有核心逻辑都已经看到了这里不得不再夸赞一下Spring的代码水平,非常的高内聚,上一个是静态的服务列表,紧挨着就是服务列表的动态更新。
这种代码对阅读者实在是太爽了,是一种享受。
1 | class RibbonClientConfiguration |
来了来了,我们本篇也可以拉下帷幕了
此处就是ILoadBalancer负载均衡器的创建逻辑
可以看到是用的ZoneAwareLoadBalancer这个实现类1
2
3class RibbonClientConfiguration
static class OverrideRestClient extends RestClientOverrideRestClient,Ribbon的核心RestClient实现类
通过引用分析,可以知道这个OverrideRestClient就是Spring提供的默认RestClient实现1
2
3
4
5
6
7
8
9
10
11
12
13class RestClientRibbonConfiguration
@Bean
@Lazy
@ConditionalOnMissingBean(AbstractLoadBalancerAwareClient.class)
public RestClient ribbonRestClient(IClientConfig config, ILoadBalancer loadBalancer,
ServerIntrospector serverIntrospector, RetryHandler retryHandler) {
RestClient client = new RibbonClientConfiguration.OverrideRestClient(config,
serverIntrospector);
client.setLoadBalancer(loadBalancer);
client.setRetryHandler(retryHandler);
return client;
}
总结
阅读Spring的代码是令人愉悦的,写法非常的高内聚,相关的逻辑紧紧的放在一处,生怕你看漏了。低耦合,控制与业务逻辑分离,非常清爽。
此处的结论在我们当前的视角是合乎逻辑的,但实际上与这个结果是有一些出入的,我们在后续的篇幅里再行探讨。
- Spring提供的默认负载均衡器是
ZoneAwareLoadBalancer
- Spring提供的默认负载均衡规则是
ZoneAvoidanceRule
- Spring提供的默认RestClient是
OverrideRestClient
- Spring提供的默认检查存活组件是
DummyPing
- Spring定时更新服务列表的组件是
PollingServerListUpdater