• 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-INFspring.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的呢?

    继续跟进

     

     

    
    
    
    
  • 相关阅读:
    217. 存在重复元素
    189. 旋转数组
    122. 买卖股票的最佳时机 II
    26. 删除排序数组中的重复项
    [剑指Offer]二进制中1的个数
    [Unity]Unity更改黑色主题(个人版)
    [Unity]限制两个物体之间的距离
    [Untiy]贪吃蛇大作战(五)——游戏主界面
    sql server 函数详解(3)数据类型转换函数和文本图像函数
    sql server 函数详解(2)数学函数
  • 原文地址:https://www.cnblogs.com/yangxiaohui227/p/12614343.html
Copyright © 2020-2023  润新知