带你了解 Ribbon 负载均衡器的实现
Spring Cloud 中 Ribbon
有在 Zuul
和 Feign
中使用,当然也可以通过在RestTemplate
的 bean 定义上添加@LoadBalanced
注解方式获得一个带有负载均衡更能的RestTemplate
。
不过实现的方法都大同小异:对HttpClient
进行封装,加上实例的”选择“(这个选择的逻辑就是我们所说的负载均衡)。
要学习某个框架的时候,最简单的方案就是:Running+Debugging。
跑就是了。
debug 不一定是为了 bug
debug 出真知
Debugging = Learning
选用 Ali Spittel 的一条推文:
以 Zuul 路由的线程栈为例
调整下顺序:
RetryableRibbonLoadBalancingHttpClient#execute(RibbonApacheHttpRequest, IClientConfig)
RetryableRibbonLoadBalancingHttpClient#executeWithRetry(...)
RetryTemplate#execute(RetryCallback<T, E>, RecoveryCallback<T>)
RetryTemplate#doExecute(RetryCallback<T, E>, RecoveryCallback<T>, RetryState)
RetryTemplate#canRetry(RetryPolicy, RetryContext)
InterceptorRetryPolicy#canRetry(RetryContext)
AbstractLoadBalancingClient#choose(String serviceId)
ZoneAwareLoadBalancer#chooseServer(Object key) //key as serviceId
BaseLoadBalancer#chooseServer(Object key)
PredicateBasedRule#choose(Object key)
AbstractServerPredicate#chooseRoundRobinAfterFiltering(List<Server> servers, Object loadBalancerKey)
AbstractServerPredicate#apply(Predicate)
分析
Zuul 收到请求经过一系列 Filter 的处理,来到 RibbonRoutingFilter
;将请求封装成 RibbonCommandContext
,然后使用 context 构建 RibbonCommand
。最终调用RibbonCommand#execute()
方法,将请求路由到下游。
RibbonCommand
持有AbstractLoadBalancerAwareClient
的对象,通过该 client 在处理请求和响应。
对于 retryable 的 client(比如此处的RetryableRibbonLoadBalancingHttpClient
), 每次处理请求的时候都会创建一个 RetryTemplate
对象来处理请求;同时根据RetryPolicy
来创建RetryContext
对象,用来保存重试的上下文,并 检查实例是否可以进行重试 。
注意重点就在这里:检查的时候如果重试次数为 0 且要检查的实例为空(说明是第一次请求),这时便会通过负载均衡器客户端(基本都是AbstractLoadBalancingClient
的子类)从后端列表择出一个实例,保存在RetryContext
中。
负载均衡器客户端使用负载均衡器(ILoadBalancer
的实现)来选择实例。每个负载均衡器都有自己的规则(IRule
的实现类),通过规则来选择实例。
IRule
的实现不是很多,
其中的ClientConfigEnabledRoundRobinRule
在RoundRobinRule
的基础上,增加了配置的接口(因为其实现了IClientConfigAware
接口)可以对规则进行配置。
某些ClientConfigEnabledRoundRobinRule
的子类了,增加了Predicate
逻辑:使用Predicate
(AbstractServerPredicate
的子类)的逻辑进行选择;而ClientConfigEnabledRoundRobinRule
只是简单的使用RoundRobinRule
进行选择。
因此选择的逻辑都是在AbstractServerPredicate
子类中,其有个特别的子类CompositePredicate
,顾名思义就是将多个逻辑整合在一起(使用Predicate#and()
将所有逻辑串联起来,达到&&
的效果),所有的逻辑检查都通过(返回true
)时,这个实例就会被选中。
那么现在要你写个自己负载均衡规则,应该知道从哪里入手了吧?:D