ITPub博客

首页 > IT职业 > IT图书 > SpringCloud Ribbon的分析

SpringCloud Ribbon的分析

IT图书 作者:古月雪 时间:2021-09-09 09:52:24 0 删除 编辑

 Spring Cloud Ribbon主要用于客户端的负载均衡。最基本的用法便是使用RestTemplate进行动态的负载均衡。我们只需要加入如下的配置便能完成客户端的负载均衡。

图片描述

@Configurationpublic class RestConfiguration {
    @Bean    @LoadBalanced    public RestTemplate restTemplate() {        return new RestTemplate();
    }
}

图片描述

图片描述

/**
 * Annotation to mark a RestTemplate bean to be configured to use a LoadBalancerClient
 * @author Spencer Gibb */@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifierpublic @interface LoadBalanced {
}

图片描述

 

这里的@LoadBalanced使得RestTemplate可以使用LoadBalancerClient,在LoadBalancerClient在这个路劲下,还存在着LoadBalancerAutoConfiguration这个配置类只要用于对LoadBalancerClient做出配置。我们就以此为入口,开始分析ribbon

 

LoadBalancer的自动化配置


图片描述

@Configuration@EnableConfigurationProperties(LoadBalancerRetryProperties.class)public class LoadBalancerAutoConfiguration {

    @LoadBalanced
    @Autowired(required = false)    

    @Bean    public SmartInitializingSingleton loadBalancedRestTemplateInitializer(            final List customizers) {        return new SmartInitializingSingleton() {
            @Override            public void afterSingletonsInstantiated() {                for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {                    for (RestTemplateCustomizer customizer : customizers) {
                        customizer.customize(restTemplate);
                    }
                }
            }
        };
    }

    @Autowired(required = false)    private List transformers = Collections.emptyList();

    @Bean
    @ConditionalOnMissingBean    public LoadBalancerRequestFactory loadBalancerRequestFactory(
            LoadBalancerClient loadBalancerClient) {        return new LoadBalancerRequestFactory(loadBalancerClient, transformers);
    }

    @Configuration
    @ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")    static class LoadBalancerInterceptorConfig {
        @Bean        public ribbonInterceptor(
                LoadBalancerClient loadBalancerClient,
                LoadBalancerRequestFactory requestFactory) {            return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
        }

        @Bean
        @ConditionalOnMissingBean        public restTemplateCustomizer(                final loadBalancerInterceptor) {            return new RestTemplateCustomizer() {
                @Override                public void customize(RestTemplate restTemplate) {
                    List list = new ArrayList(
                            restTemplate.getInterceptors());
                    list.add(loadBalancerInterceptor);
                    restTemplate.setInterceptors(list);
                }
            };
        }
    }}

图片描述

 这个配置类主要做了以下几件事

1.创建了一个LoadBalancerInterceptor的bean,用于实现对客户端发起请求时进行拦截,以实现客户端负载均衡。

2.创建了一个RestTemplateCustomizer的bean,用于给RestTemplate增加LoadBalancerInterceptor拦截器。

3.维护一个@LoadBalanced注解修饰的RestTemplate对象列表,并在这里进行维护,通过调用RestTemplateCustomizer的实例来给需要的客户端负载均衡的RestTemplate增加LoadBalancerInterceptor拦截器。

 

现在我们看下 LoadBalancerInterceptor 的拦截,我们在这里打上断点,查看一个 RestTemplate 请求是怎么被拦截的。

图片描述

图片描述

我们在 org.springframework.http.client.InterceptingClientHttpRequest.InterceptingRequestExecution#execute 发现了如下代码

if (this.iterator.hasNext()) {
                ClientHttpRequestInterceptor nextInterceptor = this.iterator.next();                return nextInterceptor.intercept(request, body, this);
            }

 这里和mybatis比较类似,都是通过责任链模式,一层层的拦截。最终就会到LoadBalancerInterceptor。现在再看LoadBalancerInterceptor的intercept方法

前两步是分别获取这个request的请求地址和ServiceName,这里截图供参考

图片描述

最开始我们说过,

@LoadBalanced使得RestTemplate可以使用LoadBalancerClient,就是在这里使用LoadBalancerClient的executor方法,做出具体的负载均衡。由于这里的LoadBalancerClient是一个接口,他具体的实现类是RibbonLoadBalancerClient,我们在这里分析具体的execute方法

图片描述

public  T execute(String serviceId, LoadBalancerRequest 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,
                serviceId), serverIntrospector(serviceId).getMetadata(server));        return execute(serviceId, ribbonServer, request);
    }

图片描述

 

第一步获取 loadBalancer,loadBalancer是定义软件负载均衡器操作的接口,有以下方法。默认配置的是使用 ZoneAwareLoadBalancer 。

图片描述

public interface ILoadBalancer {    //向负载均衡器中维护的实例列表增加服务实例
    public void addServers(List newServers);    
    //从负载均衡器中挑选出一个具体的服务实例
    public Server chooseServer(Object key);    
    //用来通知和标记负载均衡器中的某个具体实例已经停止服务,不然负载均衡器在下一次获取服务实例清单前都会认为服务实例均是正常服务的。
    public void markServerDown(Server server); //获取当前正常服务的实例列表
    public List getReachableServers();   //获取所有已知的服务实例列表,包括正常服务和停止服务实例。
    public List getAllServers();
}

图片描述

 

 

第二步是根据一定的算法去获取server,这一步也是整个ribbon的核心,关于具体的算法逻辑,我们后面分析

protected Server getServer(ILoadBalancer loadBalancer) {        if (loadBalancer == null) {            return null;
        }        return loadBalancer.chooseServer("default"); // TODO: better handling of key
    }

 

第三步 在获取到Server后包装成一个 RibbonServer

 

一个具体的server被选出来后,便可以接着请求,这时会将剩下的拦截器走完

图片描述

public  T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest request) throws IOException {
        Server server = null;        if(serviceInstance instanceof RibbonServer) {
            server = ((RibbonServer)serviceInstance).getServer();
        }        if (server == null) {            throw new IllegalStateException("No instances available for " + serviceId);
        }
        RibbonLoadBalancerContext context = this.clientFactory
                .getLoadBalancerContext(serviceId);
        RibbonStatsRecorder statsRecorder = new RibbonStatsRecorder(context, server);
      try {            T returnVal = request.apply(serviceInstance);
            statsRecorder.recordStats(returnVal);            return returnVal;
        }return null;
    }

图片描述

 

最终,创建出一个具体的请求并执行。

图片描述

public ClientHttpResponse execute(HttpRequest request, byte[] body) throws IOException {            if (this.iterator.hasNext()) {
                ClientHttpRequestInterceptor nextInterceptor = this.iterator.next();                return nextInterceptor.intercept(request, body, this);
            }            else {                ClientHttpRequest delegate = requestFactory.createRequest(request.getURI(), request.getMethod());                for (Map.Entry> entry : request.getHeaders().entrySet()) {
                    List values = entry.getValue();                    for (String value : values) {
                        delegate.getHeaders().add(entry.getKey(), value);
                    }
                }                if (body.length > 0) {
                    StreamUtils.copy(body, delegate.getBody());
                }                return delegate.execute();
            }
        }

图片描述

 

以上就是对ribbon大致流程的分析

原文出处:https://www.cnblogs.com/xmzJava/p/9583991.html

来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/3711/viewspace-2813835/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2019-07-09

  • 博文量
    160
  • 访问量
    1175