Ribbon源码解析
在前面的文章中我们了解了ribbon的基本使用和相关的负载均衡策略,但是你是否知道
Ribbon 的核心工作原理
官方文档提到了Ribbon
的核心接口, 他们共同定义了Ribbon
的行为特性
接口 | 描述 | 默认实现类 |
---|---|---|
IClientConfig | 定义Ribbon中管理配置的接口 | DefaultClientConfigImpl |
IRule | 定义Ribbon中负载均衡策略的接口 | ZoneAvoidnceRule |
IPing | 定义定期Ping 服务检查可用性的接口 | DummyPing |
ServerList<Server> | 定义获取服务列表方法的接口 | ConfigurationBasedServerList |
ServerListFilter<Server> | 定义特定期望获取服务列表方法的接口 | ZonePreferenceServerListFilter |
ILoadBalancer | 定义负载均衡选择服务的核心方法的接口 | ZoneAwareLoadBalancer |
ServerlIstUpdater | 为DynamicServerListLoadbalancer定义动态更新服务列表的接口 | PollingServerListUpdater |
可以说,
Ribbon
完全是基于这些接口建立起来的,他们就是Ribbon
的骨架,这里我们通过Ribbon
是如何使RestTemplate
达到负载均衡来一窥究竟
通过前面的文章,我们知道了如何使用Ribbon
来实现客户端负载均衡,基本的使用方式都需要注入一个RestTemplate
的Bean
,并且通过@LoadBalancer
注解来使其具备负载均衡的功能,为什么这样就可以呢? 我们来看一看这个注解的源码
/**
*Annotation to mark a RestTemplate bean to be configured to use LoadBalanceClient
*
**/
@Target({ElementType.FIELD,ElementType.PARAMETER,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Document
@Inherited
@Qualifier
public interface LoadBalancer{
}
这里注释将的比较清楚
这个注解是标记一个
RestTemplate
来使用LoadBalanceClient
那么LoadBalanceClient
又是什么呢
public interface LoadBalanceClient extends ServiceInstanceChooser{
<T> T execute(String serviceId,LoadBalanceRequest<T> request) throws IOException;
<T> T execute (String serviceId,ServiceInstance serviceInstance,LoadBalanceRequest<T> request) throws IOException;
URI reconstructURI(ServiceInstance instance,URI original);
}
显而易见,这是一个扩展自ServiceInstanceChooser
接口的 ,见名知意,这是一个服务实例选择器
public interface ServiceInstanceChooser{
ServiceInstance choose(String serviceId);
}
这两个接口源码注释都非常详细,也很容易看懂,这里稍微说明一下
ServiceInstance choose(String serviceId)
: 根据serviceId
,结合负载均衡器选择一个服务实例<T> T execute(String serviceId,LoadBalanceRequest<T> request)
:使用来自LoadBalancer
的ServiceInstance
为指定的服务执行请求<T> T execute(String serviceId,ServiceInstance,instance,LoadBalanceRequest<T> request)
:是上一个方法的重载,在实现类中可以看到他们的关系,其实就是前一个方法的细节实现URI reconstructURI(ServiceInstance instance,URI original);
:使用主机ip
和port
构建特定的URI
供内部Ribbon
使用,Ribbon
使用具有逻辑服务名称的URI
作为host
,例如:http://myservice/path/to/service
由此可见,这两个接口作用不一般,那么我们看一看他们是在哪里被初始化的,在同一个包下我们可以发现LoadBalancerAutoConfiguration
,通过名称我们就知道这就是Ribbon
功能的核心自动配置类,这里截取重要的部分
@Configuration
@ConditionalOnClass(RestTemplate.class)
@ConditionalOnBean(LoadBalancerClient.class)
@EnableConfigurationProperties(LoadBalancerRetryProperties.class)
public class LoadBalancerAutoConfiguration{
@Bean
@ConditionalOnMissingBean
public LoadBalancerRequestFactory loadBalancerRequestFactory(LoadBalancerClient client){
return new LoadBalancerRequestFactory(client,transformers);
}
@Configuration
@ConditionalOnMissingClass("org.springframework,retry.support.RetryTemplate")
static class LoadBalancerInterceptorConfig{
@Bean
public LoadBalancerInterceptor ribbonInterceptor(LoadBalancerClient loadBalancerClient,LoadBalancerRequestFactory loadBalancerRequestFactory){
return new LoadBalancerInterceptor(loadBalancerClient,loadBalancerRequestFactory);
}
@Bean
@ConditionalOnMissingBean
public RestTemplateCustomerizer(final LoadBalancerInterceptor interceptor){
return restTemplate->{
List<ClientHttpRequestInterceptor> list = new ArrayList<>(restTemplate.getInterceptors());
list.add(interceptor);
restTemplate.setInterceptors(list);
}
}
}
}
关于这源码,有以下几点
- 这个配置加载必须是当前项目环境存在
RestTemplate
实例 - 工程环境必须初始化了
LoadBalancerClient
的实现类, - 在这个配置中,
LoadBalancerRequestFactory
用于创建LoadBalancerRequest给
LoadBalancerInterceptor`使用 LoadBalancerInterceptorConfig
中则维护了LoadBalancerInterceptor
和restTemplateCustomerizer
的实例,他们的作用有以下几点LoadBalancerInterceptor
:拦截每次的http
请求,将请求绑定Ribbon
负载均衡的生命周期RestTemplateCustomerizer
: 为每个RestTemplate
绑定LoadBalancerInterceptor
拦截器
看来这个LoadBalancerInterceptor
已经很接近我们要找的东西了
public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor{
private LoadBalancerClient loadBalancer;
private LoadBalancerRequestFactory factory;
@Override
public ClientHttpResponse intercept(final HttpRequest request,fianl byte[]body,final ClientHttpRequestExecution execution)throws IOException{
final URI originalUri = request.getURI();
String serviceName = originalUri.getHost();
return this.Loadbalancer.execute(serviceName,factory.createRequest(request,body,execution));
}
}
通过源码我们可以看到
他是利用
ClientHttpRequestInterceptor
来对每次请求进行拦截,
这是spring
维护的一个拦截器,实现他的intercept
方法就可以拦截请求,从而做一些处理
这里把请求拦截下来用LoadBalancerClient
的execute
方法来处理,
比如我们前面举的例子,RestTemplate中的URI使用的是http://myservice/path/to/service
,这里的getHost()
方法实际获取到的服务名就是myservice
而LoadBalancerClient
接口只有一个实现类RibbonLoadBalancerClient
,他的execute
方法是这样的
@Override
public <T> T execute(String serviceId,LoadBalancerRequest<T>request)throws IOException{
ILoadBalancer loadBalancer = getLoadBalancer(serviceid);
Server server = getServer(loadBalancer);
if(server==null){
throw new IllegalStateException("No instances available for"+ serviceId);
}
RibbonServer ribbonServer = new RibbonServer(serviceId,server,isSecure(server,serviceIDd),serverInterceptor(serviceId).getMetadata(server));
return execute(serviceId,ribbonServer,request);
}
首先得到一个
ILoadBalancer
,
再使用它去得到一个Server
,这个Server
就是一个具体的服务实例
所以getServer(loadBalancer)
就是真正发生负载均衡的地方,以下是它的实现
protected Server getServer(ILoadBalancer loadBalancer){
if(loadBalancer==null){
return null;
}
return loadBalancer.chooseServer("default");
}
使用loadBalancer
的chooseServer
方法,我们看一看该方法的具体实现
public Server chooseServer(Object key){
if(counter==null){
counter = createCounter();
}
counter.increment();
if(rule==null){
return null;
}else{
try{
return rule.choose(key);
}catch(Exception e){
logger.warn("LoadBalancer[{}]:Error choosing server for key{}",name,key,e);
return null;
}
}
}
这里的rule.choose(key)
中rule
实际就是IRule
,到这里,负载均衡策略和http
请求的拦截终于关联起来了
既然已经分析到这里了,我们接着看负载均衡策略
负载均衡策略源码简介
这里直接给出IRule
负载均衡家族体系
当然换成这样更加直观
具体的源码就不再分析了,七种负载均衡策略篇幅较长,可以根据这个结构图去看,代码都很简单,一目了然,这里做一个简单的导读
public interface IRule {
Server choose(Object var1);
void setLoadBalancer(ILoadBalancer var1);
ILoadBalancer getLoadBalancer();
}
IRule
接口声明了三个方法
- 实现类实现
choose
方法会加入具体的负载均衡策略逻辑 setLoadBalancer()
和getLoadBalancer()
与ILoadBalancer
关联起来Ribbon
是通过ILoadBalancer
来关联IRule
的ILoadBalancer
中的chooseServer
方法会转换为调用IRule
中的choose
方法- 抽象类
AbstractLoadBalancerRule
实现了这两个方法,从而将ILoadBalancer
和IRule
关联起来,其他的实现类就是具体的负载均衡策略实现了
总结
本章主要讲解以下几点
ribbon
是如何实现负载均衡的,和背景分类IRule
和ILoadBalancer
是如何关联起来的,在AbstractLoadBalancerRule
这个抽象类中- 然后介绍了一下
Ribbon
负载均衡体系的结构类图