Ribbon与客户端Load balance
Qualifications Before Read
- Experience in RxJava/Promise
- Experience in Feign/Retrofit' inteceptor
- Familiarity with debug tools
SpringCloud层分析
我们基于拦截器机制并直接进入到了如下位置
org.springframework.cloud.netflix.feign.ribbon.LoadBalancerFeignClient
最终实现类是
com.netflix.client.AbstractLoadBalancerAwareClient#executeWithLoadBalancer
如下,非常类似于NodeJS/Netty中的await Promise
,也就是基于事件队列的实现机制,这类代码的特点就是写起来非常爽,读起来需要一定阅读量(如果阅读RxJava困难的话,可以学习前端的Promise加速理解)。
为了节约版面,我将所有的异常处理全部去掉了,代码核心在submit中,下面的代码只是一个柯里化
LoadBalancerCommand<T> command = buildLoadBalancerCommand(request, requestConfig);
// 提交后返回的是一个Promise,并没有立刻获取到
return command.submit(
// 这里的入参是一个Operation接口,初学者可能看不懂代码是怎么来回跳转的
// 实际上它本质是柯里化(Currying),用Interface模拟函数
new ServerOperation<T>() {
@Override
public Observable<T> call(Server server) {
// 此处的Server已经是**负载均衡后**的了,后面请求为原生HTTP请求
URI finalUri = reconstructURIWithServer(server, request.getUri());
S requestForServer = (S) request.replaceUri(finalUri);
return Observable
.just(AbstractLoadBalancerAwareClient.this.execute(requestForServer, requestConfig));
}
})
// 类似于前端的await,在Java中用CountDownLatch实现
.toBlocking()
.single();
整理后,获取单个Server的关键代码如下,主要在submit中这一行
// 获取当前的服务列表xml,并缓存到本地
Server server = loadBalancerContext.getServerFromLoadBalancer(loadBalancerURI, loadBalancerKey);
本人不会全屏贴代码供读者参考的,因此跳转分析技巧需要自己多练习
ILoadBalancer分析
在Ribbon中,请求关键伪代码如下
a-->b: getLoadBalancer
b-->s: chooseServer
s-->Server: IRule
整个流程与ElasticSearch等客户端框架相差不大,比如最常见的com.netflix.loadbalancer.BaseLoadBalancer
默认负载均衡算法(RoundRobinRule)与Elastic完全一样,也是通过数组取余进行计算
- RoundRobinRule: 通过数组环与Atomic自增取余