(八)springcloud从入门到放弃-Ribbon源码解析

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来实现客户端负载均衡,基本的使用方式都需要注入一个RestTemplateBean,并且通过@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):使用来自LoadBalancerServiceInstance为指定的服务执行请求
  • <T> T execute(String serviceId,ServiceInstance,instance,LoadBalanceRequest<T> request):是上一个方法的重载,在实现类中可以看到他们的关系,其实就是前一个方法的细节实现
  • URI reconstructURI(ServiceInstance instance,URI original);:使用主机ipport构建特定的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中则维护了LoadBalancerInterceptorrestTemplateCustomerizer的实例,他们的作用有以下几点
    • 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方法就可以拦截请求,从而做一些处理
这里把请求拦截下来用LoadBalancerClientexecute方法来处理,
比如我们前面举的例子,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");
}

使用loadBalancerchooseServer 方法,我们看一看该方法的具体实现

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实现了这两个方法,从而将ILoadBalancerIRule关联起来,其他的实现类就是具体的负载均衡策略实现了
总结

本章主要讲解以下几点

  • ribbon是如何实现负载均衡的,和背景分类
  • IRuleILoadBalancer是如何关联起来的,在AbstractLoadBalancerRule这个抽象类中
  • 然后介绍了一下Ribbon负载均衡体系的结构类图
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值