为了账号安全,请及时绑定邮箱和手机立即绑定

SpringCloud Ribbon的分析

标签:
Java

 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<RestTemplateCustomizer> 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<LoadBalancerRequestTransformer> 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<ClientHttpRequestInterceptor> 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> 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,
                serviceId), serverIntrospector(serviceId).getMetadata(server));        return execute(serviceId, ribbonServer, request);
    }

复制代码

 

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

复制代码

public interface ILoadBalancer {    //向负载均衡器中维护的实例列表增加服务实例
    public void addServers(List<Server> newServers);    
    //从负载均衡器中挑选出一个具体的服务实例
    public Server chooseServer(Object key);    
    //用来通知和标记负载均衡器中的某个具体实例已经停止服务,不然负载均衡器在下一次获取服务实例清单前都会认为服务实例均是正常服务的。
    public void markServerDown(Server server); //获取当前正常服务的实例列表
    public List<Server> getReachableServers();   //获取所有已知的服务实例列表,包括正常服务和停止服务实例。
    public List<Server> 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> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> 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<String, List<String>> entry : request.getHeaders().entrySet()) {
                    List<String> 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

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消