Ribbon源码分析(一)-- RestTemplate 以及自定义负载均衡算法

如果只是想看ribbon的自定义负载均衡配置,请查看: https://www.cnblogs.com/yangxiaohui227/p/13186004.html

注意:

    1.RestTemplate 所在jar为:org.springframework.web.client.RestTemplate 说明了其并不依赖springcloud

    2. 所以2个springboot项目其实是可以调用的,而并不需要依赖springCloud,如图:

   

 

 product服务:

 

 order服务:

 

 浏览器访问order服务:

 

 由此可见,服务间调用并不需要依赖springcloud组件,那么,这样调用会存在什么问题呢?

          1.ip和端口需要我们在每个调用的接口都要写死

          2.如果一个服务做了集群,这样也是只能调用写死的那个服务

解决方案:引入springCloud

改造下:product创建2个实例,架构图如下:

 

 

 

 product1的配置:

 

 product2的配置:

 

 

order服务自定义轮询算法调用product服务:

 

 调用第一次:

 

 调用第二次:

 由此可以见,负载均衡策略已经实现

然而:我们还是要拼接ip和端口,如果我们想通过服务名(spring.application.name)去调用呢:

 

 调用结果跟之前的是一致的,默认使用轮询负载均衡算法; 上面的例子虽然@LoadBalanced不是ribbon的依赖包,但请求过程中最后还是会依赖ribbon进行负载均衡

思考:上面的例子为何能通过http://product/get去访问,我们知道要访问一个服务必须知道Ip和端口,最终要变成http://ip地址:端口/get形式的url

猜想:

      1. http://product/get 请求会被拦截

      2. 通过http://product/get可以获取到服务名称为product

      3. 通过服务名称product 可以获取服务列表

     4. 通过服务列表,按照一定的算法获取一个服务,本质就是从一个list集合中获取一个服务,关键就是如何确定下标index

     5. 通过服务拿到对应的IP和端口,然后重新构建url

 

一.解决第一个猜想:http://product/get被拦截,拦截器在哪里,又是什么时候跟restTemplate 扯上关系的

    1.1 springboot自动装配机制的理解

 

 

 

 

 

 

 

 

 1.2 根据springboot自动装配机制,我们找下spring-cloud-netflix-ribbon的源码,可以看到

 

 查看内容:

  由此可见,该类会被springboot自动装配,后续的源码分析都会围绕该类来走

 

 1.3分析LoadBalancerAutoConfiguration该类

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RestTemplate.class)
@ConditionalOnBean(LoadBalancerClient.class)
@EnableConfigurationProperties(LoadBalancerRetryProperties.class)
public class LoadBalancerAutoConfiguration {

    @LoadBalanced
    @Autowired(required = false)
    private List<RestTemplate> restTemplates = Collections.emptyList();  //只有加了@LoadBalanced注解的RestTemplate就会加到该集合中

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

    @Bean
    public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(
            final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {
        return () -> restTemplateCustomizers.ifAvailable(customizers -> {
            for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
                for (RestTemplateCustomizer customizer : customizers) { 
                    customizer.customize(restTemplate); //遍历restTemplates,给其添加自定义信息
                }
            }
        });
    }

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

    @Configuration(proxyBeanMethods = false)
    @ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
    static class LoadBalancerInterceptorConfig {

        @Bean
        public LoadBalancerInterceptor ribbonInterceptor(
                LoadBalancerClient loadBalancerClient,
                LoadBalancerRequestFactory requestFactory) {
            return new LoadBalancerInterceptor(loadBalancerClient, requestFactory); //这个就是我们要找的拦截器了
        }

        @Bean
        @ConditionalOnMissingBean
        public RestTemplateCustomizer restTemplateCustomizer(
                final LoadBalancerInterceptor loadBalancerInterceptor) {
            return restTemplate -> {
                List<ClientHttpRequestInterceptor> list = new ArrayList<>(
                        restTemplate.getInterceptors());
                list.add(loadBalancerInterceptor);
                restTemplate.setInterceptors(list); //拦截器在这个里设置到restTempalte中的
            };
        }

    }

    //省略部分代码

}

 

 下面我们看看RestTemplate类的继承结构

 

 1.4 小结: springboot自动装配机制会对META-INF\spring.factories中key为EnableAutoConfiguration的类进行初始化,而ribbon和springcloud集成的文件中含有的类为:

org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration,该类初始化时会先初始化LoadBalancerAutoConfiguration,而LoadBalancerAutoConfiguration这个类是一个配置类,里面含有的@Bean注解的方法返回的类都会
被初始化和注入spring容器,该类会为含有@LoadBalanced注解的RestTemplate类添加拦截器,而拦截器最终会存到父类的一个集合中

之后流程debug调试:

二.解决猜想通过  http://product/get可以获取到服务名称为product

 

 

 

 

 

 

 

 

 

 

 //此处省略部分过程

 

 

 

 猜想三和猜想四解决:  通过服务名称product 可以获取服务列表,并通过服务列表取出一个服务

 

 

 

 

 

 //获取服务列表和和从服务列表中取出一个服务,下一篇博客将会做详细的讲解

猜想五:通过获取的服务进行url重构:

继续调试:

 

//省略部分调用

 

 

 

 

 

 

 

 

 至此可以发现,ribbon底层主要是将我们的服务名称替换为ip和端口

附加:RestTemplate的底层调用:

 

 响应结果是如何转换成String的呢?

继续跟进

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 



posted @ 2020-04-01 18:02  yangxiaohui227  阅读(899)  评论(0)    收藏  举报