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

    xiaoxiao2023-10-16  24

    Ribbon源码解析

    在前面的文章中我们了解了ribbon的基本使用和相关的负载均衡策略,但是你是否知道

    Ribbon 的核心工作原理

    官方文档提到了Ribbon的核心接口, 他们共同定义了Ribbon的行为特性

    接口描述默认实现类IClientConfig定义Ribbon中管理配置的接口DefaultClientConfigImplIRule定义Ribbon中负载均衡策略的接口ZoneAvoidnceRuleIPing定义定期Ping 服务检查可用性的接口DummyPingServerList<Server>定义获取服务列表方法的接口ConfigurationBasedServerListServerListFilter<Server>定义特定期望获取服务列表方法的接口ZonePreferenceServerListFilterILoadBalancer定义负载均衡选择服务的核心方法的接口ZoneAwareLoadBalancerServerlIstUpdater为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负载均衡体系的结构类图
    最新回复(0)