前言

这篇博文着重讲源码,对于这种比较复杂的源码,我喜欢截图分析,因为图可以作标注,比直接贴源码要方便和醒目。本篇博文会有大量的图片,显得文章会很长,其实文字是不多的。建议读者自己配合源码阅读。注意版本保持一致,我的版本下面会给出。

我的很多分析在图片上会有标注,读者请关注图片上的被红框部分以及标注部分。

一、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原理。

先分析这一部分:

图1-初始化

首先我们要知道,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源码分析(一)相关推荐

  1. 深入分析Ribbon源码分析

    本文来分析下Ribbon源码 文章目录 Ribbon源码分析 负载均衡器 AbstractLoadBalancer BaseLoadBalancer DynamicServerListLoadBala ...

  2. Java微服务组件Spring cloud ribbon源码分析

    微服务组件Spring Cloud Ribbon源码分析_哔哩哔哩_bilibili Ribbon源码分析 | ProcessOn免费在线作图,在线流程图,在线思维导图 | 1.什么是ribbon? ...

  3. ribbon源码分析之自定义配置、全局配置

    在上一文EnableDiscoveryClient没用了?Zookeeper是怎么和springboot配合做服务注册中心的?讲过了zk是怎么做服务注册和服务发现的,同时在spring.factori ...

  4. SpringCloud之Feign源码分析

    启动时Feign的处理 启动类上使用了@EnableFeignClients注解,我们来看下这个注解在哪里使用了,使用idea只要在EnableFeignClients类上按住command同时点击类 ...

  5. Spring Cloud部分源码分析Eureka,Ribbon,Feign,Zuul

    Eureka SpringCloud Eureka使用NetFlix Eureka来实现的,它包括了服务端组件和客户端组件,并且都是用java 编写的. Eureka服务端就是服务注册中心, Eure ...

  6. SpringCloud ribbon源码

    1. 基本使用 server.port=8080spring.application.name=ribbon-clientxxx-server.ribbon.listOfServers=localho ...

  7. Spring Cloud源码分析(二)Ribbon(续)

    因文章长度限制,故分为两篇.上一篇:<Spring Cloud源码分析(二)Ribbon> 负载均衡策略 通过上一篇对Ribbon的源码解读,我们已经对Ribbon实现的负载均衡器以及其中 ...

  8. Spring Cloud源码分析——Ribbon客户端负载均衡

    年前聊了Eureka和Zookeeper的区别,然后微服务架构系列就鸽了三个多月,一直沉迷逛B站,无法自拔.最近公司复工,工作状态慢慢恢复(又是元气满满地划水).本文从以下3个方面进行分析(参考了翟永 ...

  9. 升级SpringCloud到Hoxton.SR3后使用Fegin出现jackson反序列化失败,源码分析,原因lombok版本升级

    关键词 Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct insta ...

  10. 【SpringCloud微服务】第3章 服务治理SpringCloudEureka(五)——Eureka源码分析

    2.8 Eureka 源码分析   首先,对于服务注册中心.服务提供者.服务消费者这三个主要元素来说,后两者(也就是Eureka客户端)在整个运行机制中是大部分通信行为的主动发起者,而注册中心主要是处 ...

最新文章

  1. mac 怎么查找大于200m的文件_U盘无法拷贝大于4GB的文件怎么办?
  2. 产品分析报告|读书新贵——《网易蜗牛读书》
  3. c语言编写心理测试,求各位大神赐教!我做了一个“心理测试的答题卷”编程,总共有1...
  4. python文件处理seek_python文件操作 seek(),tell()
  5. Shell脚本中函数位置参数的用法笔记
  6. java字节码指令简介(仅了解)
  7. ukf实测信号的预测 matlab,无迹卡尔曼滤波UKF无线传感器网络定位跟踪matlab源码实现.pdf...
  8. g2o:一种图优化的C++框架
  9. CCF中学生计算机程序设计入门篇练习2.4.2(NOI 1002 三角形) pascal
  10. (原創) 如何設計除頻器? (SOC) (Verilog) (MegaCore)
  11. MongoDB中balancer操作
  12. 本地获取jqdata的港股通资金数据保存为sql数据库
  13. 张峥、小白谈GPT与人工智能:可能是好事,也可能不是
  14. android应用白屏闪退,解决 APP启动白屏黑屏问题
  15. 单片机c语言置位程序流程图,单片机c语言教程第十二章--C51开关分支语句
  16. Cris 带你快速入门 Flink
  17. 无线网首选dns服务器怎么设置,dns服务器设置(192.168.1.1的首选dns)
  18. 服务器系统装QC软件,HP-QC环境安装设置
  19. 高铁检测试验软件,京沪高铁试验检测项目
  20. Maya次世代武器全流程 Maya低模zbrush雕刻高模substance painter上材质贴图讲解

热门文章

  1. 超详细Python进行信用评分卡建模【kaggle的give me some credit数据集】【风控建模】
  2. 恢复oracle数据步骤,通过数据泵expdp、impdp方式备份与还原/恢复 Oracle数据库(详细过程)-Oracle...
  3. 容器技术Docker K8s 34 容器服务ACK基础与进阶-安全管理
  4. 【易实战】Spring Cloud Greenwich Ribbon:负载均衡的服务调用
  5. 计算机控制技术数据存储器有,计算机控制技术复习资料.doc
  6. android ram压力测试,android用memtester内存压力测试
  7. 410.分割数组的最大值
  8. 区分指针数组和数组指针
  9. 单链表的反转(C++)
  10. 2020 字节跳动 面经