SpringCloud之Ribbon源码分析(一)
前言
这篇博文着重讲源码,对于这种比较复杂的源码,我喜欢截图分析,因为图可以作标注,比直接贴源码要方便和醒目。本篇博文会有大量的图片,显得文章会很长,其实文字是不多的。建议读者自己配合源码阅读。注意版本保持一致,我的版本下面会给出。
我的很多分析在图片上会有标注,读者请关注图片上的被红框部分以及标注部分。
一、Ribbon的作用
Ribbon的作用是负载均衡。使用Ribbon实现负载均衡时,基本用法是注入一个RestTemplate,并使用@LoadBalanced注解标注RestTemplate,从而使RestTemplate具备负载均衡的能力。
当Spring容器启动时,使用@LoadBalanced注解修饰的RestTemplate会被添加拦截器,拦截器中使用了LoadBalancerClient处理请求,从而达到负载均衡的目的。那么LoadBalancerClient内部是如何做到的呢?下面我们通过源码分析的方式来剖析Ribbon负载均衡的工作原理。
二、demo
2.1 pom.xml配置增加ribbon依赖
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-ribbon</artifactId><version>2.2.3.RELEASE</version></dependency>
2.2 application.properties配置{spring.application.name}.ribbon.listOfServers
在没有配置注册中心的情况下,使用{spring.application.name}.ribbon.listOfServers配置写死服务端节点。
# 配置指定服务的提供者的地址列表
spring-cloud-order-service.ribbon.listOfServers=\localhost:8080,localhost:8082
2.3 应用
三、源码分析一:RibbonLoadBalanceClient装配过程
网上的图:
接下来我们会根据上图来分析ribbon原理。
先分析这一部分:
首先我们要知道,RibbonLoadBalanceClient是ribbon负载的重要类。至于RibbonLoadBalanceClient在ribbon负载是怎样一个流程,我们后面再说,这一部分先看它是如何添加到spring中的。
3.1RibbonAutoConfiguration配置类
看呀,spring-cloud-netflix-ribbon-2.2.3.RELEASE.jar下面的spring.factories,有如下配置:
如上图我说的,SPI!自动装载!
在RibbonAutoConfiguration配置类上,我们可以看到下面这部分
@AutoConfigureBefore({ LoadBalancerAutoConfiguration.class,AsyncLoadBalancerAutoConfiguration.class })
表示RibbonAutoConfiguration.class会在LoadBalancerAutoConfiguration.class之前装载。
注解 @AutoConfigureBefore 和 @AutoConfigureAfter 的用途
3.2 LoadBalancerAutoConfiguration配置类
LoadBalancerAutoConfiguration配置类的生成情况:
接下来我们看LoadBalancerAutoConfiguration.class类:
@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();@Autowired(required = false)private List<LoadBalancerRequestTransformer> transformers = Collections.emptyList();@Beanpublic SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {return () -> restTemplateCustomizers.ifAvailable(customizers -> {for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {for (RestTemplateCustomizer customizer : customizers) {customizer.customize(restTemplate);}}});}@Bean@ConditionalOnMissingBeanpublic LoadBalancerRequestFactory loadBalancerRequestFactory(LoadBalancerClient loadBalancerClient) {return new LoadBalancerRequestFactory(loadBalancerClient, this.transformers);}@Configuration(proxyBeanMethods = false)@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")static class LoadBalancerInterceptorConfig {@Beanpublic LoadBalancerInterceptor ribbonInterceptor(LoadBalancerClient loadBalancerClient,LoadBalancerRequestFactory requestFactory) {return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);}@Bean@ConditionalOnMissingBeanpublic RestTemplateCustomizer restTemplateCustomizer(final LoadBalancerInterceptor loadBalancerInterceptor) {return restTemplate -> {List<ClientHttpRequestInterceptor> list = new ArrayList<>(restTemplate.getInterceptors());list.add(loadBalancerInterceptor);restTemplate.setInterceptors(list);};}}/*** Auto configuration for retry mechanism.*/@Configuration(proxyBeanMethods = false)@ConditionalOnClass(RetryTemplate.class)public static class RetryAutoConfiguration {@Bean@ConditionalOnMissingBeanpublic LoadBalancedRetryFactory loadBalancedRetryFactory() {return new LoadBalancedRetryFactory() {};}}/*** Auto configuration for retry intercepting mechanism.*/@Configuration(proxyBeanMethods = false)@ConditionalOnClass(RetryTemplate.class)public static class RetryInterceptorAutoConfiguration {@Bean@ConditionalOnMissingBeanpublic RetryLoadBalancerInterceptor ribbonInterceptor(LoadBalancerClient loadBalancerClient,LoadBalancerRetryProperties properties,LoadBalancerRequestFactory requestFactory,LoadBalancedRetryFactory loadBalancedRetryFactory) {return new RetryLoadBalancerInterceptor(loadBalancerClient, properties,requestFactory, loadBalancedRetryFactory);}@Bean@ConditionalOnMissingBeanpublic RestTemplateCustomizer restTemplateCustomizer(final RetryLoadBalancerInterceptor loadBalancerInterceptor) {return restTemplate -> {List<ClientHttpRequestInterceptor> list = new ArrayList<>(restTemplate.getInterceptors());list.add(loadBalancerInterceptor);restTemplate.setInterceptors(list);};}}}
在该自动化配置类中,主要做了下面三件事:
- 创建了一个LoadBalancerInterceptor的Bean,用于实现对客户端发起请求时进行拦截,以实现客户端负载均衡。
- 创建了一个RestTemplateCustomizer的Bean,用于给RestTemplate增加LoadBalancerInterceptor拦截器。
- 维护了一个被@LoadBalanced注解修饰的RestTemplate对象列表,并在这里进行初始化,通过调用RestTemplateCustomizer的实例来给需要客户端负载均衡的RestTemplate增加LoadBalancerInterceptor拦截器。
到这里,对比图1分析下来,流程是不是还挺清晰的呀。
一个疑问:
那么我打个断点调试一下:
可以看到我@Bean生成的LoadBalanceInterceptor生成的bean对象是@6409,在下面
restTemplateCustomizer方法中,只要看看LoadBalanceInterceptor对象是不是@6409就行,然而它不是:
它是@6413,所以这俩不是一个 LoadBalanceInterceptor对象,至于 restTemplateCustomizer()方法闯进来的@6413LoadBalanceInterceptor对象是从哪里生成以及注入的,后面再说,值得一提的是这两个对象的属性引用是一致的:
扩展:
3.3@Qualifier和@LoadBalanced
Ribbon中@LoadBalanced注解的原理
四、从restTemplate#getForObject来分析Ribbon源码
首先我们知道ribbon是通过拦截器来实现负载的。那么发起一个请求,是怎么如何到拦截器的呢?带着这个问题,我们来看一下源码。
看一下RestTemplate类结构图:
看下调用流程:
restTemplate#getForObject——>restTemplate#execute——>restTemplate#doExecute
接下来我们来看restTemplate#doExecute源码:
4.1 createRequest方法——得到一个ClientHttpRequest对象
我们着重看#createRequest方法,和ClientHttpRequest#execute方法。
createRequest方法是在父类中实现的,父类——HttpAccessor.class:
继续看HttpAccessor#createRequest方法:
点进InterceptingHttpAccessor#getRequestFactory查看:
其中,getRequestFactory方法代码如下,其中getInterceptors是获得当前客户端请求的所有拦截
器,需要注意的是,这里的拦截器,就包含LoadBalancerInterceptor。
来解答上图②问题,这里我们前面分析过了,来看:
来看红圈③,创建出来的是InterceptorClientHttpRequestFactory工厂类,点createRequest方法:
想看它的类继承图:
它的父类是AbstactBufferingClientHttpRequest,来看父类AbstactBufferingClientHttpRequest代码:
所以得到的是InterceptingClientHttpRequest!
4.2 分析request.execute()方法,执行请求逻辑
搞明白上面的流程了,那么接下来就是到下一阶段了,查看request.execute():
类结构图:
进入AbstractBufferingClientHttpRequest#executeInternal方法:
进入到InterceptingClientHttpRequest# executeInternal:
为了证实我上面的说法,我们打断点可以看到,确实是含有拦截器的:
既然分析到这里了,我们不妨就进到LoadBalancerInterceptor拦截器里去瞅瞅:
接着分析:
上图②:深入解析
先看一下ZoneAwareLoadBalancer:
代码追踪到这里,其他的我们先放一放。接下来看服务在LoadBancer中是怎么缓存进来的。
4.3 LoadBancer如何缓存服务
接下来的逻辑就在下图红色虚线框中了:
想看BaseLoadBalancer类:
我们可以看到BaseLoadBalancer类有allServerList和upServerList属性,看名字就知道,allServerList-所有服务列表,upServerList-目前在线的服务列表。
这里是在哪里设置,从哪里获取到的值?
接下来的流程就是下图的深蓝色实线框内的步骤了:
请看下面:
继续看父类DynamicServerListLoadBalancer类的有参构造函数:
进入红框①代码:
默认30s更新一次
进入红框②代码:
到这里,这一块就分析结束了。
4.4 ribbon如何初始化几个重要的对象
把视线再移到这一块:
看一下RibbonClientConfiguration是怎么初始化下面这几个对象的:
PollingServerListUpdater:
PollingServerListUpdater通过这个@Bean方法注入进去的。
这个方法的参数是config,config的bean是怎么创建的呢,请看下面:
这里先分析到这。
4.5 DynamicServerListLoadBalancer#updateListOfServers方法
下面回到updateListOfServers方法,来讲它的代码:
点进去瞅一下是怎么更新的:
4.6 心跳
再初始化BaseLoadBalancer构造函数时,有下面这一句代码:
这里我就把PingUrl.isAlive()代码贴一下,以后我们写心跳可以参考:
public boolean isAlive(Server server) {String urlStr = "";if (this.isSecure) {urlStr = "https://";} else {urlStr = "http://";}urlStr = urlStr + server.getId();urlStr = urlStr + this.getPingAppendString();boolean isAlive = false;HttpClient httpClient = new DefaultHttpClient();HttpUriRequest getRequest = new HttpGet(urlStr);String content = null;try {HttpResponse response = httpClient.execute(getRequest);content = EntityUtils.toString(response.getEntity());isAlive = response.getStatusLine().getStatusCode() == 200;if (this.getExpectedContent() != null) {LOGGER.debug("content:" + content);if (content == null) {isAlive = false;} else if (content.equals(this.getExpectedContent())) {isAlive = true;} else {isAlive = false;}}} catch (IOException var11) {var11.printStackTrace();} finally {getRequest.abort();}return isAlive;
}
好了,本篇博文先到这里结束。下一篇我们还将继续分析ribbon,敬请关注。
SpringCloud之Ribbon源码分析(一)相关推荐
- 深入分析Ribbon源码分析
本文来分析下Ribbon源码 文章目录 Ribbon源码分析 负载均衡器 AbstractLoadBalancer BaseLoadBalancer DynamicServerListLoadBala ...
- Java微服务组件Spring cloud ribbon源码分析
微服务组件Spring Cloud Ribbon源码分析_哔哩哔哩_bilibili Ribbon源码分析 | ProcessOn免费在线作图,在线流程图,在线思维导图 | 1.什么是ribbon? ...
- ribbon源码分析之自定义配置、全局配置
在上一文EnableDiscoveryClient没用了?Zookeeper是怎么和springboot配合做服务注册中心的?讲过了zk是怎么做服务注册和服务发现的,同时在spring.factori ...
- SpringCloud之Feign源码分析
启动时Feign的处理 启动类上使用了@EnableFeignClients注解,我们来看下这个注解在哪里使用了,使用idea只要在EnableFeignClients类上按住command同时点击类 ...
- Spring Cloud部分源码分析Eureka,Ribbon,Feign,Zuul
Eureka SpringCloud Eureka使用NetFlix Eureka来实现的,它包括了服务端组件和客户端组件,并且都是用java 编写的. Eureka服务端就是服务注册中心, Eure ...
- SpringCloud ribbon源码
1. 基本使用 server.port=8080spring.application.name=ribbon-clientxxx-server.ribbon.listOfServers=localho ...
- Spring Cloud源码分析(二)Ribbon(续)
因文章长度限制,故分为两篇.上一篇:<Spring Cloud源码分析(二)Ribbon> 负载均衡策略 通过上一篇对Ribbon的源码解读,我们已经对Ribbon实现的负载均衡器以及其中 ...
- Spring Cloud源码分析——Ribbon客户端负载均衡
年前聊了Eureka和Zookeeper的区别,然后微服务架构系列就鸽了三个多月,一直沉迷逛B站,无法自拔.最近公司复工,工作状态慢慢恢复(又是元气满满地划水).本文从以下3个方面进行分析(参考了翟永 ...
- 升级SpringCloud到Hoxton.SR3后使用Fegin出现jackson反序列化失败,源码分析,原因lombok版本升级
关键词 Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct insta ...
- 【SpringCloud微服务】第3章 服务治理SpringCloudEureka(五)——Eureka源码分析
2.8 Eureka 源码分析 首先,对于服务注册中心.服务提供者.服务消费者这三个主要元素来说,后两者(也就是Eureka客户端)在整个运行机制中是大部分通信行为的主动发起者,而注册中心主要是处 ...
最新文章
- mac 怎么查找大于200m的文件_U盘无法拷贝大于4GB的文件怎么办?
- 产品分析报告|读书新贵——《网易蜗牛读书》
- c语言编写心理测试,求各位大神赐教!我做了一个“心理测试的答题卷”编程,总共有1...
- python文件处理seek_python文件操作 seek(),tell()
- Shell脚本中函数位置参数的用法笔记
- java字节码指令简介(仅了解)
- ukf实测信号的预测 matlab,无迹卡尔曼滤波UKF无线传感器网络定位跟踪matlab源码实现.pdf...
- g2o:一种图优化的C++框架
- CCF中学生计算机程序设计入门篇练习2.4.2(NOI 1002 三角形) pascal
- (原創) 如何設計除頻器? (SOC) (Verilog) (MegaCore)
- MongoDB中balancer操作
- 本地获取jqdata的港股通资金数据保存为sql数据库
- 张峥、小白谈GPT与人工智能:可能是好事,也可能不是
- android应用白屏闪退,解决 APP启动白屏黑屏问题
- 单片机c语言置位程序流程图,单片机c语言教程第十二章--C51开关分支语句
- Cris 带你快速入门 Flink
- 无线网首选dns服务器怎么设置,dns服务器设置(192.168.1.1的首选dns)
- 服务器系统装QC软件,HP-QC环境安装设置
- 高铁检测试验软件,京沪高铁试验检测项目
- Maya次世代武器全流程 Maya低模zbrush雕刻高模substance painter上材质贴图讲解
热门文章
- 超详细Python进行信用评分卡建模【kaggle的give me some credit数据集】【风控建模】
- 恢复oracle数据步骤,通过数据泵expdp、impdp方式备份与还原/恢复 Oracle数据库(详细过程)-Oracle...
- 容器技术Docker K8s 34 容器服务ACK基础与进阶-安全管理
- 【易实战】Spring Cloud Greenwich Ribbon:负载均衡的服务调用
- 计算机控制技术数据存储器有,计算机控制技术复习资料.doc
- android ram压力测试,android用memtester内存压力测试
- 410.分割数组的最大值
- 区分指针数组和数组指针
- 单链表的反转(C++)
- 2020 字节跳动 面经