SpringCloud之Ribbon客户端负载原理分析(一)-LoadBalancerInterceptor原理分析_Lazy·R的博客-程序员秘密

技术标签: 微服务  

SpringCloud之Ribbon客户端负载应用及原理分析
SpringCloud之Ribbon客户端负载原理分析(一)-LoadBalancerInterceptor原理分析
SpringCloud之Ribbon客户端负载原理分析(二)-RibbonLoadBalancerClient原理分析
SpringCloud之Ribbon客户端负载原理分析(三)-ILoadBalancer原理分析

本文主要内容:

从@LoadBalanced作为切入点,了解LoadBalancerInterceptorr的添加过程及作用

  1. 前期准备知识:了解restemplate的基本使用和@Qualifier注解特性
  2. LoadBalancerInterceptor原理分析
    • 了解LoadBalancerInterceptor的初始化过程
    • 了解LoadBalancerInterceptor如何拦截请求处理

一、前期准备知识

在学习@LoadBalanced的原理之前,有两个知识点需要了解

  • restemplate基本使用
  • @Qualifier注解的使用和特性

这两个知识点都比较简单,详细介绍可以自行学习,本文不做过多说明,只会说一下其中的关键点

1.1、restemplate基本使用

RestTemplate是Spring提供的用于访问Rest服务的客户端,RestTemplate提供了多种便捷访问远程Http服务的方法,能够大大提高客户端的编写效率。简单说,就是通过RestTemplate进行服务间的调用

String result = restTemplate.getForObject("http://spring-cloud-order-service/orders", String.class);

在这里插入图片描述

1.2、了解@Qualifier注解的特性

@Qualifier注解的特性,有两点需要特别关注:

  • 当指定@Qualifier注解的 value() 属性时:@Qualfier注解进行过滤的同时,还会按照 Bean 名称(Name)或者别名(Alias)等于value进行Bean筛选
  • 当不指定@Qualifier注解的 value() 属性时:会按照@Qualfier元注解过滤,即:只要添加了@Qualfier注解就会被扫描到

我们以下面代码为例进行理解:

  • TestController的restemplates1:因为指定@Qualifier(“restemplateA”),所以只会注入restemplateA
  • TestController的restemplates2:因为@Qualifier()未指定value值,所以会注入所有增加了@Qualifier注解的RestTemplate,即:会注入restemplateA、restemplateB

如果RestemplateConfiguration中的RestTemplate都不增加 @Qualifier注解,TestController在注入RestTemplate时会报错

@Configuration
public class RestemplateConfiguration {
    

    @Qualifier
    @Bean("restemplateA")
    RestTemplate testClass1(){
    
        return new RestTemplate();
    }

    @Qualifier
    @Bean("restemplateB")
    RestTemplate testClass2(){
    
        return new RestTemplate();
    }
}

@RestController
public class TestController {
    

    // 只会注入名字为restemplateA的RestTemplate
    @Qualifier("restemplateA")
    @Autowired
    List<RestTemplate> restemplatesList1= Collections.emptyList();

    @Qualifier
    @Autowired
    List<RestTemplate> restemplatesList2= Collections.emptyList();

    @GetMapping("/test")
    public void test(){
    
        System.out.println("restemplatesList1-->"+restemplatesList1);
        System.out.println("restemplatesList2-->"+restemplatesList2);
    }
}

我们查看@LoadBalanced注解的源码,可以发现它就添加了@Qualifier注解,所以@LoadBalanced注解具有上面@Qualifier注解的两个的特性

@Target({
     ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier
public @interface LoadBalanced {
    }

二、LoadBalancerInterceptor原理分析

做好前面的基础准备以后,我们开始进行Ribbbon原理分析。
首先,我们是通过@LoadBalanced注解开启客户端负载功能的,而@LoadBalanced注解的作用就是给restemplate添加一个LoadBalancerInterceptor,接下来我们就从LoadBalancerInterceptor初始化过程开始进行原理分析。

2.1、LoadBalancerInterceptor初始化过程

2.1.1、LoadBalancerInterceptor初始化过程示意图

下面是LoadBalancerInterceptor初始化过程时,方法调用的示意图,可以配合源码分析观看:
在这里插入图片描述

下面通过源码具体分析Spring Cloud Ribbon的初始化过程

2.1.2、LoadBalancerAutoConfiguration源码分析

基于Springboot的自动装配,Spring会自动加载LoadBalancerAutoConfiguration,而LoadBalancerAutoConfiguration会完成以下操作

  1. 获取所有增加了@LoadBalanced注解的RestTemplate
  2. 初始化一个LoadBalancerInterceptor拦截器:ribbonInterceptor
  3. 初始化一个RestTemplateCustomizer:它的作用是为RestTemplate添加LoadBalancerInterceptor拦截器
  4. 调用(3)中RestTemplateCustomizer的方法,为(1)中的每个RestTemplate,添加一个(2)生成的LoadBalancerInterceptor拦截器

LoadBalancerAutoConfiguration源码如下,可以查看方法注解了解核心方法的作用:

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

    /**
	 * 注入所有增加了@LoadBalanced注解的RestTemplate(基于@Qualifier注解特性)
	 */
	@LoadBalanced
	@Autowired(required = false)
	private List<RestTemplate> restTemplates = Collections.emptyList();

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

     /**
	 * 1、获取所有的RestTemplateCustomizer
	 * 2、调用RestTemplateCustomizer中的方法,为restTemplatetians中的每一个RestTemplate添加拦截器
	 * 其中一个RestTemplateCustomizer:就是为restTemplate添加LoadBalancerInterceptor拦截器
	 */
	@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);
				}
			}
		});
	}

    /**
	 * 初始化一个LoadBalancerRequestFactory,其中的参数LoadBalancerClient由自动注入
	 */
	@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 {
    

         /**
         * 初始化一个LoadBalancerInterceptor,构造两个参数:
         *  1、loadBalancerClient
         *  2、LoadBalancerRequestFactory
         */
		@Bean
		public LoadBalancerInterceptor ribbonInterceptor(
				LoadBalancerClient loadBalancerClient,
				LoadBalancerRequestFactory requestFactory) {
    
			return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
		}

        /**
         * 初始化一个RestTemplateCustomizerr,传入的构造参数是上面初始化的的LoadBalancerInterceptor
         * RestTemplateCustomizer的作用:为restTemplate添加LoadBalancerInterceptor拦截器
         */
		@Bean
		@ConditionalOnMissingBean
		public RestTemplateCustomizer restTemplateCustomizer(
				final LoadBalancerInterceptor loadBalancerInterceptor) {
    
			return restTemplate -> {
    
				List<ClientHttpRequestInterceptor> list = new ArrayList<>(
						restTemplate.getInterceptors());
				list.add(loadBalancerInterceptor);
				restTemplate.setInterceptors(list);
			};
		}

	}
    
   // 其他代码省略
}

上面的源码注解中写到:在初始化ribbonInterceptor时,会自动注入一个LoadBalancerClient,而这个LoadBalancerClient是在RibbonAutoConfiguration中完成初始化的

2.2.3、RibbonAutoConfiguration源码分析

RibbonAutoConfiguration的源码分析:

  1. @AutoConfigureBefore的作用:定义要求RibbonAutoConfiguration需要在LoadBalancerAutoConfiguration之前被加载
  2. loadBalancerClient()方法:生成一个LoadBalancerClient客户端,即:RibbonLoadBalancerClient

RibbonAutoConfiguration.class源码:

@Configuration
@Conditional(RibbonAutoConfiguration.RibbonClassesConditions.class)
@RibbonClients
@AutoConfigureAfter(
		name = "org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration")
@AutoConfigureBefore({
     LoadBalancerAutoConfiguration.class,
		AsyncLoadBalancerAutoConfiguration.class })
@EnableConfigurationProperties({
     RibbonEagerLoadProperties.class,
		ServerIntrospectorProperties.class })
public class RibbonAutoConfiguration {
    

	@Autowired(required = false)
	private List<RibbonClientSpecification> configurations = new ArrayList<>();

	@Autowired
	private RibbonEagerLoadProperties ribbonEagerLoadProperties;

	@Bean
	public HasFeatures ribbonFeature() {
    
		return HasFeatures.namedFeature("Ribbon", Ribbon.class);
	}

	@Bean
	@ConditionalOnMissingBean
	public SpringClientFactory springClientFactory() {
    
		SpringClientFactory factory = new SpringClientFactory();
		factory.setConfigurations(this.configurations);
		return factory;
	}

    // 初始化一个LoadBalancerClient,即:RibbonLoadBalancerClient
	@Bean
	@ConditionalOnMissingBean(LoadBalancerClient.class)
	public LoadBalancerClient loadBalancerClient() {
    
		return new RibbonLoadBalancerClient(springClientFactory());
	}

	@Bean
	@ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate")
	@ConditionalOnMissingBean
	public LoadBalancedRetryFactory loadBalancedRetryPolicyFactory(
			final SpringClientFactory clientFactory) {
    
		return new RibbonLoadBalancedRetryFactory(clientFactory);
	}

	@Bean
	@ConditionalOnMissingBean
	public PropertiesFactory propertiesFactory() {
    
		return new PropertiesFactory();
	}

	@Bean
	@ConditionalOnProperty("ribbon.eager-load.enabled")
	public RibbonApplicationContextInitializer ribbonApplicationContextInitializer() {
    
		return new RibbonApplicationContextInitializer(springClientFactory(),
				ribbonEagerLoadProperties.getClients());
	}

	// 代码省略
}

LoadBalancerClient最终会处理restemplate的请求数据,而LoadBalancerClient具有以下作用:

  1. 存储并维护服务列表,
  2. 根据路由规则,通过servicename获取一个服务地址
  3. 将请求数据发送到服务地址

2.2、LoadBalancerInterceptor拦截请求处理

2.2.1、LoadBalancerInterceptor的拦截过程示意图

在这里插入图片描述

2.2.2、LoadBalancerInterceptor源码分析

通过上面的自动装配会为每一个添加了@LoadBalancer的resttemplate添加一个LoadBalancerInterceptor拦截器(ribbonInterceptor),而这个LoadBalancerInterceptor.intercept()会拦截restemplate的所有请求,并进行以下处理:

  1. 通过构造方法传入一个LoadBalancerClient
  2. 拦截restemplate请求数据,解析url中的serviceName
  3. 将请求数据封装成LoadBalancerRequest
  4. 将serviceName和LoadBalancerRequest交由LoadBalancerClient进行处理
public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {
    

    // 通过构造方法传入的LoadBalancerClient
	private LoadBalancerClient loadBalancer;

	private LoadBalancerRequestFactory requestFactory;

	public LoadBalancerInterceptor(LoadBalancerClient loadBalancer,
			LoadBalancerRequestFactory requestFactory) {
    
		this.loadBalancer = loadBalancer;
		this.requestFactory = requestFactory;
	}

    /**
      * LoadBalancerInterceptor构造方法
      */
	public LoadBalancerInterceptor(LoadBalancerClient loadBalancer) {
    
		// for backwards compatibility
		this(loadBalancer, new LoadBalancerRequestFactory(loadBalancer));
	}

     /**
      * 拦截restemplate的所有请求,进行处理
      */
	@Override
	public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
			final ClientHttpRequestExecution execution) throws IOException {
    
        // 1、获取请求url
		final URI originalUri = request.getURI();
        // 2、解析serviceName
		String serviceName = originalUri.getHost();
		Assert.state(serviceName != null,
				"Request URI does not contain a valid hostname: " + originalUri);
		 // 3、将请求数据封装成LoadBalancerRequest<T>
         // 4、将serviceName和LoadBalancerRequest交由LoadBalancerClient进行处理
        return this.loadBalancer.execute(serviceName,
				this.requestFactory.createRequest(request, body, execution));
	}

}

这里是通过调用LoadBalancerRequestFactory.createRequest()方法创建LoadBalancerRequest

2.2.3、LoadBalancerRequestFactory源码分析

LoadBalancerRequestFactory.createRequest()方法:

  • 通过匿名内部类的方法,创建一个LoadBalancerRequest
  • 通过Lambda表达式实现了LoadBalancerRequest.apply()方法
public class LoadBalancerRequestFactory {
    

	private LoadBalancerClient loadBalancer;

	private List<LoadBalancerRequestTransformer> transformers;

	public LoadBalancerRequestFactory(LoadBalancerClient loadBalancer,
			List<LoadBalancerRequestTransformer> transformers) {
    
		this.loadBalancer = loadBalancer;
		this.transformers = transformers;
	}

	public LoadBalancerRequestFactory(LoadBalancerClient loadBalancer) {
    
		this.loadBalancer = loadBalancer;
	}

    /*
     * 通过匿名内部类的方法,创建一个LoadBalancerRequest
     * 通过Lambda表达式实现了LoadBalancerRequest.apply()方法
     */
	public LoadBalancerRequest<ClientHttpResponse> createRequest(
			final HttpRequest request, final byte[] body,
			final ClientHttpRequestExecution execution) {
    
		return instance -> {
    
			HttpRequest serviceRequest = new ServiceRequestWrapper(request, instance,
					this.loadBalancer);
			if (this.transformers != null) {
    
				for (LoadBalancerRequestTransformer transformer : this.transformers) {
    
					serviceRequest = transformer.transformRequest(serviceRequest,
							instance);
				}
			}
			return execution.execute(serviceRequest, body);
		};
	}
}

2.2.4、LoadBalancerInterceptor的总结

LoadBalancerInterceptor拦截Restemplate的所有请求,从中解析出serviceName,并创建一个LoadBalancerRequest交给RibbonLoadBalancerClient处理

下篇文章开始分析:RibbonLoadBalancerClient

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/HRebel/article/details/110760970

智能推荐

为什么深度学习图像分类里的图片的输入大小都是224*224呢?_图像分割 输入图像大小多少合适_Miss_zhuo_的博客-程序员秘密

在论文中进行各类方法的比较时,要求使用同样的数据集。而为了公平的比较,网络的输入大小通常都是224*224的大小,那为什么呢?我们都知道,一个图像分类模型,在图像中经历了下面的流程。从输入image-&gt;卷积和池化-&gt;最后一层的feature map-&gt;全连接层-&gt;损失函数层softmax loss从输入到最后一个卷积特征feature map,就是进行信息抽象的过程,然后就经过全连接层/全局池化层的变换进行分类了,这个feature map的大小,可以是3*3,5*5

UTXO 和 Account 模型对比_跨链技术践行者的博客-程序员秘密

在当前区块链世界中,主要有两种记录保存方式,UTXO 模式(Unspent Transaction Output) 和 Account 模式。Bitcoin 采用的是 UTXO 模型,Ethereum 采用的 Account 模型,同样 CITA 也采用了 Account 模型。Bitcoin 的设计初衷是点对点的电子现金系统,在比特币中,每个交易消耗之前交易生成的 UTXO 然后生成新的 U...

杰理AC692N系列MUSIC 模式下STOP键处理_WEIXIN- FY1174801586的博客-程序员秘密

STOP键往往是音乐停止键,与PP键不同的是stop按键在音乐播放时按下停止后再按PP按键会从第一首播放。

openssl 证书验证_openssl 验证证书_wiggens的博客-程序员秘密

本节中我们快速浏览一下证书验证的主干代码。读者可以采用上节中生成的VC工程进行验证。下面列出关键部分代码,为方便阅读,仅保留与证书验证强相关的代码,去掉了诸如变量定义、错误处理、资源释放等非主要代码,并修改了排版格式。// 初始入口为 apps\verify.c 中的 MAIN 函数// 为利于代码阅读,下面尝试将相关代码放在一起(采用函数调用栈的形式,被调用函数的代码排版缩进...

Md5Util 加密工具类_md5util.encrypt_RylynnKang的博客-程序员秘密

在各种应用系统的开发中,经常需要存储用户信息,很多地方都要存储用户密码,而将用户密码直接存储在服务器上显然是不安全的,本文简要介绍工作中常用的 MD5加密算法,希望能抛砖引玉。(一)消息摘要简介一个消息摘要就是一个数据块的数字指纹。即对一个任意长度的一个数据块进行计算,产生一个唯一指印(对于SHA1是产生一个20字节的二进制数组)。消息摘要是一种与消息认证码结合使用以确保消息完整性的技术。主要使用单

习题8.18 编一程序,输入月份号,输出该月的英文月名。例如,输入“3”,则输出“March“,要求用指针数组处理_ajwx_1127的博客-程序员秘密

#include &lt;stdio.h&gt;int main(){ char a[12][15]={"January","Feburary","March","April","May","June","July","August","Setpermber","October","Norvermber","December"}; char *p[12]; int i,n; printf("请输入任一数字型月份:"); scanf("%d",&amp;n);

随便推点

动态规划经典例题-国王的金矿问题_北海道恋人的博客-程序员秘密

金矿问题问题概述:有一位国王拥有5座金矿,每座金矿的黄金储量不同,需要参与挖掘的工人人数也不同。例如有的金矿储量是500kg黄金,需 要5个工人来挖掘;有的金矿储量是200kg黄金,需要3个工人来挖 掘…… 如果参与挖矿的工人的总数是10。每座金矿要么全挖,要么不挖,不能 派出一半人挖取一半的金矿。要求用程序求出,要想得到尽可能多的黄 金,应该选择挖取哪几座金矿?什么是动态规划:动态规划:将复杂问题简化为规模较小的子问题,再从简单的子问题自底向上一步一步递推,最终得到复杂问题的最优解思路:利用

NOIP2016提高组复赛day2 †组合数问题_dance_in_the_dark的博客-程序员秘密

DescriptionData ConstraintSolution由于k很小,最多也就分解成两个质因数,所以我们先将k分解成两个质因数x,y。然后统计出1~n的每个数是x或y的几次幂,设为a[i]。一次组合数运算相当于a[i]-a[j]-a[i-j]。判断一下即可。Code#include<iostream>#include<cmath>#include<cstring>#include<c

图像(视频)拼接(一)_图像拼接柱形变换_ssuper_bin的博客-程序员秘密

图像拼接&视频拼接前瞻研一课程终于结束了,有时间写下在这半年里自己除课程外玩过的一些小玩意,先写一篇关于图像拼接以及360度视频拼接前瞻性的一些内容,暑假如果自己有时间还会把最近玩的一些内容发到博客上。图像拼接图像拼接已经算玩的很透的东西了,一般来说无非就有两种常用的:柱形拼接:用变焦镜头,将开阔地带展示出来,使用于范围宽阔的建筑或建筑群、海岸线、茫茫沙漠等;球型拼接:采用了陷阱

训练网络的实用trick总结_提升网络效果的trick_ReLuJie的博客-程序员秘密

原文链接:http://karpathy.github.io/2019/04/25/recipe/博文链接:https://mp.weixin.qq.com/s?__biz=MzIzNjc1NzUzMw==&amp;mid=2247519817&amp;idx=3&amp;sn=f484657f9adf12acce2c2f3f4dfaedd6&amp;pass_ticket=GWaOqxcpN...

作为iOS开发者,你不可错过的资源_chenghuyi8987的博客-程序员秘密

转载请注明出处:@芳仔小脚印http://my.oschina.net/joanfen/blog/268467像比较大的社区站点就不会分享了,主要是个人博客,开源工具,好的文章,书等等,侧重于iOS开发者有人问我如何高效地学习,或是如何自学,资源等等,说实在的,我都不知道自己学习是否高效,...

Elastic-job系列(一)-------- 搭建Esjob控制台_es job console_请叫我猿叔叔的博客-程序员秘密

一、简介 之前项目用的是springboot的@schedule注解,但是无法监控任务的执行情况,并且在集群的时候任务不好管理。网上搜索的有几种分布式任务调度框架,目前准备研究当当的elastic-job。后面准备再看下xxl-job。学习下,然后运用到项目中。二、搭建步骤2.1 下载工程并打gz包github地址:https://github.com/e...

推荐文章

热门文章

相关标签