带你了解 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