负载均衡组件Ribbon核心-@LoadBalanced-下

news2024/11/25 11:36:20

引言

书接上篇 负载均衡组件Ribbon核心-@LoadBalanced-上 我们讲完了理解@LoadBalanced注解的知识准备,接下来就是@LoadBalanced注解详解。

LoadBalancerAutoConfiguration 激活

@LoadBalanced 注解功能起点来至LoadBalancerAutoConfiguration自动配置类,SpringBoot容器启动的时,根据加入的依赖(spring-web/spring-cloud-ribbon),LoadBalancerAutoConfiguration 配置类会被激活

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

}

激活条件: 

@ConditionalOnClass(RestTemplate.class):spring-web 依赖引入RestTemplate类
@ConditionalOnBean(LoadBalancerClient.class):要求容器内有LoadBalancerClient Bean实例

这是,肯定有朋友疑惑:@LoadBalanced功能起点不是这个配置类么,哪来LoadBalancerClient Bean实例。有疑惑是对的,@LoadBalanced 注解主宰的是Ribbon负载均衡功能,而不是整个Ribbon操作,LoadBalancerClient Bean 在Ribbon 加载前就已经配置被创建了。且看

 这个就是Ribbon组件引入的启动器,里面有个Ribbion自动配置类:RibbonAutoConfiguration,看下源码

@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 {

   	@Bean
	@ConditionalOnMissingBean(LoadBalancerClient.class)
	public LoadBalancerClient loadBalancerClient() {
		return new RibbonLoadBalancerClient(springClientFactory());
	}

}

再看头顶贴上的注解:

@AutoConfigureBefore({ LoadBalancerAutoConfiguration.class,
		AsyncLoadBalancerAutoConfiguration.class })

真相来了,RibbonAutoConfiguration 配置类的启动早于LoadBalancerAutoConfiguration,也就是说,RibbonAutoConfiguration 配置的LoadBalancerClient  实例在LoadBalancerAutoConfiguration启动前就已经实例化好了。

RestTemplate 负载均衡能力集成

当了解了LoadBalancerAutoConfiguration配置类如何进行激活,接下就看如何将负载均衡的能力集成在RestTemplate实例中。总结一下需要走下面3步:

1>RestTemplate 收集  2>RestTemplate 定制器   3>RestTemplate拦截注入

步骤1:RestTemplate 收集

我们从LoadBalancerAutoConfiguration配置类restTemplates集合属性发现端倪。

public class LoadBalancerAutoConfiguration {

	@LoadBalanced
	@Autowired(required = false)
	private List<RestTemplate> restTemplates = Collections.emptyList();

    ...
}

从源码上看,Spring容器会收集所有RestTemplate实例,并注入restTemplates 中。此时问题来了,它怎么知道哪些RestTemplate 类需要添加负载均衡功能?这是就用到@Qualifier 知识点了(不了解朋友可以看上篇)。

先看@LoadBalanced注解的源码

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

}

发现它头上有个元注解:@Qualifier, 意味着@LoadBalanced 拥有@Qualifier 注解所有功能,

	@LoadBalanced
	@Autowired(required = false)
	private List<RestTemplate> restTemplates = Collections.emptyList();

-------------------------------------------------------------------------------
	//@LoadBalanced
    @Qualifier   //等价
	@Autowired(required = false)
	private List<RestTemplate> restTemplates = Collections.emptyList();

很明显,它用上了@Qualifier 注解 Bean筛选标记功能。

步骤2:RestTemplate 定制器

步骤1完成后,可以得到待加负载均衡逻辑的restTemplates 集合。接下来就是集成了,Spring提供了XxxCustomizer类实现实例定制,下面是RestTemplate定制器接口源码

public interface RestTemplateCustomizer {

	void customize(RestTemplate restTemplate);

}

接口只有一个方法:customize, 参数为RestTemplate,很明显看出它就用来改造RestTemplate 实例的。

继续往下,LoadBalancerAutoConfiguration配置类下有个LoadBalancerInterceptorConfig 静态内部类

public class LoadBalancerAutoConfiguration {
	@Configuration(proxyBeanMethods = false)
	@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
	static class LoadBalancerInterceptorConfig {
        ....

		@Bean
		@ConditionalOnMissingBean
		public RestTemplateCustomizer restTemplateCustomizer(
				final LoadBalancerInterceptor loadBalancerInterceptor) {
			return restTemplate -> {
				List<ClientHttpRequestInterceptor> list = new ArrayList<>(
						restTemplate.getInterceptors());
				list.add(loadBalancerInterceptor);
				restTemplate.setInterceptors(list);
			};
		}

	}
	}
}

类内部,定义了用于构建RestTemplateCustomizer 定制器实例方法:restTemplateCustomizer(loadBalancerInterceptor),参数是loadBalancerInterceptor 拦截器。后续将该拦截器添加到restTemplate 实例中。

list.add(loadBalancerInterceptor);
restTemplate.setInterceptors(list);

定制逻辑完成,接下来回答2个问题,RestTemplate 类负载均衡逻辑就通了

问题1:RestTemplate 定制器怎么执行的?

问题2:LoadBalancerInterceptor 拦截器做啥逻辑?

这里先回答问题1,问题2在步骤3详解

RestTemplate 定制器使用 SmartInitializingSingleton 接口 (不理解/忘记可以看上篇)实现调用。

当Spring容器所有单例Bean实例初始化成功之后,实现SmartInitializingSingleton 接口Bean实例afterSingletonsInstantiated 方法会被回调。

public class LoadBalancerAutoConfiguration {
    ....
	@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);
				}
			}
		});
	}
    ....
}

源码逻辑:收集容器中所有RestTemplateCustomizer  定制器,对步骤1收集到的LoadBalancerAutoConfiguration.this.restTemplates RestTemplate对象集合,一一定制。

步骤3:RestTemplate 拦截器

还是LoadBalancerAutoConfiguration配置类下有个LoadBalancerInterceptorConfig 静态内部类

public class LoadBalancerAutoConfiguration {
	@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);
		}
       ....
	}
	}
}

类内部,定义ribbonInterceptor 实例方法,参数为loadBalancerClient,requestFactory ,目的地是创建LoadBalancerInterceptor 拦截器实例。

步骤2中问题:LoadBalancerInterceptor 拦截器做了啥操作?

上篇文章我们了解过ClientHttpRequestInterceptor拦截器,当RestTemplate 调用getForEntity/getForObject方法发起http请求时,会执行拦截逻辑,LoadBalancerInterceptor  拦截器就是ClientHttpRequestInterceptor 实现类

public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {
    ....
	@Override
	public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
			final ClientHttpRequestExecution execution) throws IOException {
		final URI originalUri = request.getURI();
		String serviceName = originalUri.getHost();
		Assert.state(serviceName != null,
				"Request URI does not contain a valid hostname: " + originalUri);
		return this.loadBalancer.execute(serviceName,
				this.requestFactory.createRequest(request, body, execution));
	}

}

大白话结论

综上3个步骤,得出结论:

当贴有@LoadBalanced 注解的RestTemplate发起getForEntity/getForObject 会执行RestTemplateCustomizer 定制器绑定LoadBalancerInterceptor 拦截器 intercept 方法,然后返回ClientHttpResponse 结果

负载均衡后续

到这,肯定有好学的小伙伴问,最后一步的负载均衡怎么调用啊?这个问题已经超出本篇文章的讨论了,如果想深究的朋友,还可以继续:

	@Override
	public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
			final ClientHttpRequestExecution execution) throws IOException {
		final URI originalUri = request.getURI();
		String serviceName = originalUri.getHost();
		Assert.state(serviceName != null,
				"Request URI does not contain a valid hostname: " + originalUri);
		return this.loadBalancer.execute(serviceName,
				this.requestFactory.createRequest(request, body, execution));
	}

LoadBalancerInterceptor 拦截器 intercept 方法执行,调用了this.loadBalancer.execute 实现远程服务调用,负载均衡的调用效果,就由这个方法完成。再深入,就进到:

public class RibbonLoadBalancerClient implements LoadBalancerClient {

   	public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint)
			throws IOException {
		ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
		Server server = getServer(loadBalancer, hint);
		if (server == null) {
			throw new IllegalStateException("No instances available for " + serviceId);
		}
		RibbonServer ribbonServer = new RibbonServer(serviceId, server,
				isSecure(server, serviceId),
				serverIntrospector(serviceId).getMetadata(server));

		return execute(serviceId, ribbonServer, request);
	}
}

最核心逻辑:

Server server = getServer(loadBalancer, hint);
	protected Server getServer(ILoadBalancer loadBalancer, Object hint) {
		if (loadBalancer == null) {
			return null;
		}
		// Use 'default' on a null hint, or just pass it on?
		return loadBalancer.chooseServer(hint != null ? hint : "default");
	}

这里的chooseServer方法,就是根据Ribbon配置的负载均衡策略来调用服务了,默认是

public class ZoneAwareLoadBalancer<T extends Server> extends DynamicServerListLoadBalancer<T> {

   public Server chooseServer(Object key) {
     ...
   }
}

好了,到这,就真的结束了~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/53036.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

uni-app 超详细教程(三)(从菜鸟到大佬)

本文中内容为&#xff1a; 1. 支付功能&#xff08;微信支付&#xff0c;支付宝支付&#xff09; 2. 项目打包&#xff1a;&#xff08;APP打包&#xff0c;H5打包&#xff0c;微信小程序打包&#xff09; 一&#xff0c;uni - app 的支付功能 一、微信支付 1、登录微信开…

如何在Ubuntu20.04上安装RDP远程

计算机最有意思的事情&#xff0c;就是你可以用任何方式去实现跨设备连接。例如google通过webrtc实现远程桌面&#xff0c;Linux则是常用ssh等。在远程桌面上一般分为windows的RDP和Unix/Linux的VNC。 常规在windows上winr输入mstsc,我们通过微软的RDP技术去远程计算机。RDP和…

C#Lambda让代码变得更加简洁而优雅

Using a lambda expression&#xff0c;we can make the code more compact and elegant。   在使用lambda表达式时&#xff0c;可以使代码更加简洁和优雅。 Lambda&#xff0c;希腊字母λ&#xff0c;在C#编程语言中&#xff0c;被引入为Lambda表达式&#xff0c;表示为匿名…

使用[阿里问题定位神器]Arthas入门

目录 注意 安装 在线安装 离线安装 目前我接触到的实用命令 dashboard heapdump thread jad stack trace 注意 arthas本身有一定的性能消耗&#xff0c;所以生产环境小心使用 arthas本身有一定的性能消耗&#xff0c;所以生产环境小心使用 arthas本身有一定的性能…

【Linux】万字总结Linux 基本指令,绝对详细!!!

文章目录 Linux 基本指令 ls 指令 alias 指令 cd指令 pwd 指令 clear指令 touch 指令 mkdir 指令&#xff08;重要&#xff09; rmdir指令 && rm 指令&#xff08;重要&#xff09;&#xff1a; man指令&#xff08;重要&#xff09; cp指令&#xff08;重…

闭关三个月,腾讯大咖手写Framework最新源码笔记,从基础到进阶直接封神

什么是Android Framework 我们首先给出Android Framework的定义&#xff0c;然后再对该定义给出详细的解释。 Android Framework包含三个内容&#xff1a;服务端、客户端、linux驱动 服务端 Android Framework服务端包括两个很重要的类&#xff1a;WindowManagerService (W…

实验(六):定时器实验

一、实验目的与任务 实验目的&#xff1a; 1&#xff0e;掌握定时/计数器的中断法工作原理&#xff1b; 2&#xff0e;熟悉C51编程与调试方法。 任务&#xff1a; 1. 运行Keil开发环境&#xff0c;完成定时器软件编程&#xff1b; 2. 建立Proteus仿真模型&#xff1b; 3&#x…

day15_面向对象的三大特征之一(继承)

继承的概述 Java是对现实社会的模拟&#xff0c;现实中有儿子可以继承父亲的财产&#xff0c;因此有了富二代的。 java中多个类中存在相同属性和行为时&#xff0c;将这些内容抽取到单独一个类中&#xff0c;那么多个类中无需再定义这些属性和行为&#xff0c;只需要和抽取出来…

CSDNtop1全栈接口测试教程 jmeter接口测试,接口自动化测试

测试时优先对其进行结构化拆分&#xff0c;将测试整体拆分为各个场景 创建线程组&#xff0c;简单控制器&#xff0c;HTTP请求默认值&#xff0c;HTTP信息头管理器 将测试目标结构化&#xff0c;可以更好地管理测试框架和整合其他组件&#xff0c;有利于反馈工作 添加HTTP请求…

如何做好自动化测试?揭开测试项目团队的自动化实践过程……

稍具测试规模的项目团队皆想引进自动化测试&#xff0c;然而动手实现自动化测试的团队却不多&#xff0c;未能真正实施的原因多种多样&#xff0c;有扼杀在摇篮里的&#xff0c;有写了后弃之不用。那么是不是所有的业务都适合自动化测试呢&#xff1f;下面就介绍下自己在项目中…

超级好用的笔记工具------Typora 如何修改Typora 中图片保存的位置

用了这么多的笔记、最后还是选择了Typora。真的是很不错呐。一些私密的笔记、比如公司内部资料。放到网页多多少少是不安全的。还是放到本地安全的多。 1、使用Typora 做的小笔记 1.1 目录情况 这个可以按照自己的进度或者时间节点自行分级 1.2 某一个页面的具体设计 2、基本…

react18 通过redux 做一个简单的状态管理基站

我们打开react项目 在终端输入 npm install redux --saveredux就进来了 这里 我们引入了 redux 但其实 有一个 redux 和一个 react-redux 两者区别在于 redux 是一个js的状态管理容器 而react-redux 则提供了 更多便于react开发的状态管理方法 然后我们在项目的src目录下创…

4-FreeRTOS队列、互斥、信号量

1-队列 队列&#xff08;我对队列的理解就是上体育课&#xff0c;排队这种&#xff09;是任务之间通信的一种方式。队列可以用于任务和任务之间或者中断和任务之间消息的接收与发送。在多数情况下&#xff0c;他们消息缓冲是按照FIFO&#xff08;先进先出&#xff09;原则。也…

文本处理方式方法

概述 从今天开始&#xff0c;我们将开启一段自然语言处理的流程&#xff0c;自然语言可以让来处理、理解以及运用人类的语言&#xff0c;实现机器语言和人类语言之间的沟通桥梁。 文本处理 我们正在进行文本处理的时候&#xff0c;经常会用到文本长度不一致的情况&#xff0c…

Microsoft SQL Server 图书管理数据库的建立

文章目录题目描述创建数据库使用数据库创建三个表外码的表示形式结果展示题目描述 – 新建 “图书管理数据库" – 其中包含三个关系 – 图书&#xff08;编号&#xff0c;图书名&#xff0c;作者&#xff0c;出版社&#xff0c;类型&#xff0c;单价&#xff09; – 借阅…

ASP.NET Core 3.1系列(16)——Entity Framework Core之Code First

1、前言 前一篇博客介绍了EFCore中的DB First开发模式&#xff0c;该模式可以根据数据库生成实体类和数据库上下文&#xff0c;因此适用于数据库已经存在的场景。而与之相对应的&#xff0c;Code First主要是根据自定义的实体类和数据库上下文反向构建数据库&#xff0c;因此也…

操作系统02_进程管理_同步互斥信号量_PV操作_死锁---软考高级系统架构师007

存储管理可以分为固定存储管理和分页存储管理。 现在固定存储管理已经不用也不考,但要知道因为固定存储管理指的是整存整取 也就是把一整个程序,比如说10G的游戏全部都存到内存里 这样的话是非常占用内存的,这个固定存储管理现在已经不用了。 然后这里我们主要看分页存储管: …

网页去色变黑白+网页黑白恢复为彩色

前言 特定节日&#xff0c;你会发现网页和app首页都会变成灰色&#xff0c;以此来表达我们的哀思之情。 好奇宝宝想知道各个网站都是使用哪些小技巧来做出这种效果的&#xff08;由彩变灰&#xff0c;由灰变彩&#xff09;&#xff0c;于是稍微学习了一下… 由灰变彩 稍微想…

USDP集群服务器宕机后集群及组件重启

USDP集群的其中2服务器意外宕机&#xff0c;其中包括一台USDP管理服务节点主机和工作节点主机&#xff0c;服务器重新启动后&#xff0c;USDP智能大数据平台无法登录&#xff0c;启动UDSP服务&#xff08;/opt/usdp-srv/usdp/bin/start-udp-server.sh&#xff09;后可以登录&am…

Go1.9.3跑GinDemo

Gin 1. 简介 1.1. 介绍 Gin是一个golang的微框架&#xff0c;封装比较优雅&#xff0c;API友好&#xff0c;源码注释比较明确&#xff0c;具有快速灵活&#xff0c;容错方便等特点 对于golang而言&#xff0c;web框架的依赖要远比Python&#xff0c;Java之类的要小。自身的n…