如果只是想看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的呢?
继续跟进