Spring-Cloud-Loadblancer详细分析_2

@LoadBalancerClients

终于分析到了此注解的作用,它是实现不同服务之间的配置隔离的关键

@Configuration(proxyBeanMethods = false)
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE })
@Documented
@Import(LoadBalancerClientConfigurationRegistrar.class)
public @interface LoadBalancerClients {LoadBalancerClient[] value() default {};/*** {@link LoadBalancerClientConfigurationRegistrar} creates a* {@link LoadBalancerClientSpecification} with this as an argument. These in turn are* added as default contexts in {@link LoadBalancerClientFactory}. Configuration* defined in these classes are used as defaults if values aren't defined via* {@link LoadBalancerClient#configuration()}* @return classes for default configurations*/Class<?>[] defaultConfiguration() default {};}@Configuration(proxyBeanMethods = false)
@Import(LoadBalancerClientConfigurationRegistrar.class)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LoadBalancerClient {/*** Synonym for name (the name of the client).** @see #name()* @return the name of the load balancer client*/@AliasFor("name")String value() default "";/*** The name of the load balancer client, uniquely identifying a set of client* resources, including a load balancer.* @return the name of the load balancer client*/@AliasFor("value")String name() default "";/*** A custom <code>@Configuration</code> for the load balancer client. Can contain* override <code>@Bean</code> definition for the pieces that make up the client.** @see LoadBalancerClientConfiguration for the defaults* @return configuration classes for the load balancer client.*/Class<?>[] configuration() default {};}
  • LoadBalancerClients 就是其实就是多个LoadBalancerClient
  • LoadBalancerClient 相当于一个负载均衡配置,name/value 就是 serviceId,configuration 就是负载均衡配置
  • 通过@Import(LoadBalancerClientConfigurationRegistrar.class)来进行注入

LoadBalancerClientConfigurationRegistrar

public class LoadBalancerClientConfigurationRegistrar implements ImportBeanDefinitionRegistrar {private static String getClientName(Map<String, Object> client) {if (client == null) {return null;}String value = (String) client.get("value");if (!StringUtils.hasText(value)) {value = (String) client.get("name");}if (StringUtils.hasText(value)) {return value;}throw new IllegalStateException("Either 'name' or 'value' must be provided in @LoadBalancerClient");}private static void registerClientConfiguration(BeanDefinitionRegistry registry, Object name,Object configuration) {BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(LoadBalancerClientSpecification.class);builder.addConstructorArgValue(name);builder.addConstructorArgValue(configuration);//每个LoadBalancerClient其实就是LoadBalancerClientSpecificationregistry.registerBeanDefinition(name + ".LoadBalancerClientSpecification", builder.getBeanDefinition());}@Overridepublic void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {Map<String, Object> attrs = metadata.getAnnotationAttributes(LoadBalancerClients.class.getName(), true);//获取LoadBalancerClients 注解的属性if (attrs != null && attrs.containsKey("value")) {//value属性为LoadBalancerClient注解,可以给configuration赋值,填写需要的配置类,比如RandomLoadBalancerConfig.classAnnotationAttributes[] clients = (AnnotationAttributes[]) attrs.get("value");for (AnnotationAttributes client : clients) {//将配置类 注册到当前容器registerClientConfiguration(registry, getClientName(client), client.get("configuration"));}}if (attrs != null && attrs.containsKey("defaultConfiguration")) {String name;if (metadata.hasEnclosingClass()) {name = "default." + metadata.getEnclosingClassName();}else {name = "default." + metadata.getClassName();}//如果defaultConfiguration有配置,当做默认配置注册中,并赋值给所有容器registerClientConfiguration(registry, name, attrs.get("defaultConfiguration"));}//处理@LoadBalancerClient注解,逻辑和上面LoadBalancerClients的相同Map<String, Object> client = metadata.getAnnotationAttributes(LoadBalancerClient.class.getName(), true);String name = getClientName(client);if (name != null) {//configuration 属性的类 注册到容器中registerClientConfiguration(registry, name, client.get("configuration"));}}}
  • LoadBalancerClients有一个默认的配置属性 Class<?>[] configuration() default {};这个属性的类以default. 开头来命名
  • LoadBalancerClients含有多个LoadBalancerClient,属性valueserviceId属性 Class<?>[] configuration() default {};是当前serviceId名字下的配置
  • LoadBalancerClients和每个LoadBalancerClientconfiguration都会被封装以clientName为维度的LoadBalancerClientSpecification,注册到当前容器中
  • 注意! 这时注册的是BeanDefinition类型的元数据,还没有开始真正的实例化bean!真正的实例化是在子容器创建的时候会将父容器添加进去,也相当于子容器也含有了,然后会启动子容器这时,就会进行真正的实例化。
  • 最终注入到LoadBalancerAutoConfiguration配置类的属性configurations,用于LoadBalancerClientFactory 的实例化
  • LoadBalancerClientFactory实例化的时候,就会获取LoadBalancerClientSpecification的对象了
    在这里插入图片描述在这里插入图片描述
    可以看到将LoadBalancerAutoConfigurationBlockingLoadBalancerClientAutoConfiguration注入,包装成LoadBalancerClientSpecification类型

LoadBalancerClientFactory

再看一眼LoadBalancerClientFactorybean实例生成的过程
在这里插入图片描述
可以看到将LoadBalancerClientSpecification类型的LoadBalancerAutoConfigurationBlockingLoadBalancerClientAutoConfiguration通过setConfiguations方法注入了进入

setConfiguations方法是在父类NamedContextFactory中执行的,稍微会分析NamedContextFactory,这里先分析LoadBalancerClientFactory 的结构

public class LoadBalancerClientFactory extends NamedContextFactory<LoadBalancerClientSpecification>implements ReactiveLoadBalancer.Factory<ServiceInstance> {private static final Log log = LogFactory.getLog(LoadBalancerClientFactory.class);/*** Property source name for load balancer.*/public static final String NAMESPACE = "loadbalancer";/*** Property for client name within the load balancer namespace.*/public static final String PROPERTY_NAME = NAMESPACE + ".client.name";private final LoadBalancerClientsProperties properties;public LoadBalancerClientFactory(LoadBalancerClientsProperties properties) {//记住这个LoadBalancerClientConfiguration,被当成了默认配置类注入到 NamedContextFactory ,也就是每个子容器都会有这个配置类super(LoadBalancerClientConfiguration.class, NAMESPACE, PROPERTY_NAME);this.properties = properties;}public static String getName(Environment environment) {return environment.getProperty(PROPERTY_NAME);}@Overridepublic ReactiveLoadBalancer<ServiceInstance> getInstance(String serviceId) {return getInstance(serviceId, ReactorServiceInstanceLoadBalancer.class);}@Overridepublic LoadBalancerProperties getProperties(String serviceId) {if (properties == null) {if (log.isWarnEnabled()) {log.warn("LoadBalancerClientsProperties is null. Please use the new constructor.");}return null;}if (serviceId == null || !properties.getClients().containsKey(serviceId)) {// no specific client properties, return defaultreturn properties;}// because specifics are overlayed on top of defaults, everything in `properties`,// unless overridden, is in `clientsProperties`return properties.getClients().get(serviceId);}}

既然LoadBalancerClientConfiguration注入到NamedContextFactory中,我们就分析此配置类

LoadBalancerClientConfiguration

@Configuration(proxyBeanMethods = false)
@ConditionalOnDiscoveryEnabled
public class LoadBalancerClientConfiguration {private static final int REACTIVE_SERVICE_INSTANCE_SUPPLIER_ORDER = 193827465;/*** 怕读者忘了此配置的注入过程,这里再说一下* 上文说的LoadBalancerAutoConfiguration配置类中,创建了 LoadBalancerClientFactory 的bean对象,存在于父容器中* 在此bean创建的过程中,会通过构造方法将此配置LoadBalancerClientConfiguration转入到LoadBalancerClientFactory中* 然后会当做默认配置类注册到 NamedContextFactory 的子容器中,这样每个子容器都拥有* 我们也可以自己实现这个ReactorLoadBalancer类型的bean,覆盖此配置** */@Bean@ConditionalOnMissingBeanpublic ReactorLoadBalancer<ServiceInstance> reactorServiceInstanceLoadBalancer(Environment environment,LoadBalancerClientFactory loadBalancerClientFactory) {// 获取当前子容器的名称(也是服务名)String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);return new RoundRobinLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);}//省略...@Configuration(proxyBeanMethods = false)@ConditionalOnBlockingDiscoveryEnabled@Order(REACTIVE_SERVICE_INSTANCE_SUPPLIER_ORDER + 1)public static class BlockingSupportConfiguration {//ServiceInstanceListSupplier来查询ServiceInstance服务列表,能看到这里是DiscoveryClient提供的。这样就和注册中心关联了@Bean@ConditionalOnBean(DiscoveryClient.class)@ConditionalOnMissingBean@Conditional(DefaultConfigurationCondition.class)public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(ConfigurableApplicationContext context) {return ServiceInstanceListSupplier.builder().withBlockingDiscoveryClient().withCaching().build(context);}}//省略...}

LoadBalancerClientConfiguration的作用分析完了,在上文中

public LoadBalancerClientFactory(LoadBalancerClientsProperties properties) {//记住这个LoadBalancerClientConfiguration,被当成了默认配置类注入到 NamedContextFactory ,也就是每个子容器都会有这个配置类super(LoadBalancerClientConfiguration.class, NAMESPACE, PROPERTY_NAME);this.properties = properties;
}

LoadBalancerClientConfiguration是传给了父类,LoadBalancerClientFactory 的结构

public class LoadBalancerClientFactory extends NamedContextFactory<LoadBalancerClientSpecification>implements ReactiveLoadBalancer.Factory<ServiceInstance> {..}

LoadBalancerClientFactory生成bean的过程中,调用完构造方法后,又执行了clientFactory.setConfigurations(this.configurations.getIfAvailable(Collections::emptyList)),此方法是在父类NamedContextFactory执行的

看一下clientFactory.setConfigurations(this.configurations.getIfAvailable(Collections::emptyList))执行结果
在这里插入图片描述

下面我们要分析父类NamedContextFactory,非常的重要

NamedContextFactory

public abstract class NamedContextFactory<C extends NamedContextFactory.Specification>implements DisposableBean, ApplicationContextAware {private final String propertySourceName;private final String propertyName;private Map<String, AnnotationConfigApplicationContext> contexts = new ConcurrentHashMap<>();private Map<String, C> configurations = new ConcurrentHashMap<>();private ApplicationContext parent;private Class<?> defaultConfigType;/*** defaultConfigType是刚才文中说的子类LoadBalancerClientFactory创建时注入的。类型为LoadBalancerClientConfiguration* */public NamedContextFactory(Class<?> defaultConfigType, String propertySourceName, String propertyName) {this.defaultConfigType = defaultConfigType;this.propertySourceName = propertySourceName;this.propertyName = propertyName;}@Overridepublic void setApplicationContext(ApplicationContext parent) throws BeansException {this.parent = parent;}public ApplicationContext getParent() {return parent;}/*** configurations为LoadBalancerAutoConfiguration和BlockingLoadBalancerClientAutoConfiguration,* 文中刚才分析过* */public void setConfigurations(List<C> configurations) {for (C client : configurations) {this.configurations.put(client.getName(), client);}}public Set<String> getContextNames() {return new HashSet<>(this.contexts.keySet());}@Overridepublic void destroy() {Collection<AnnotationConfigApplicationContext> values = this.contexts.values();for (AnnotationConfigApplicationContext context : values) {// This can fail, but it never throws an exception (you see stack traces// logged as WARN).context.close();}this.contexts.clear();}protected AnnotationConfigApplicationContext getContext(String name) {if (!this.contexts.containsKey(name)) {synchronized (this.contexts) {if (!this.contexts.containsKey(name)) {//如果不存在则先创建容器this.contexts.put(name, createContext(name));}}}return this.contexts.get(name);}protected AnnotationConfigApplicationContext createContext(String name) {AnnotationConfigApplicationContext context;//创建容器if (this.parent != null) {DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();if (parent instanceof ConfigurableApplicationContext) {beanFactory.setBeanClassLoader(((ConfigurableApplicationContext) parent).getBeanFactory().getBeanClassLoader());}else {beanFactory.setBeanClassLoader(parent.getClassLoader());}context = new AnnotationConfigApplicationContext(beanFactory);context.setClassLoader(this.parent.getClassLoader());}else {context = new AnnotationConfigApplicationContext();}//configurations就是LoadBalancerClientSpecification类型的LoadBalancerAutoConfiguration和BlockingLoadBalancerClientAutoConfiguration//这里是将@LoadBalancerClient对应的负载均衡配置注册到对应的容器中//由以上可知通过此步我们可以使用@LoadBalancerClient自定义负载均衡策略//如果不自定义的话,这里为falseif (this.configurations.containsKey(name)) {for (Class<?> configuration : this.configurations.get(name).getConfiguration()) {context.register(configuration);}}//也就是将configurations中LoadBalancerClientSpecification类型的LoadBalancerAutoConfiguration和BlockingLoadBalancerClientAutoConfiguration注册到容器中//这样每个容器就拥有了for (Map.Entry<String, C> entry : this.configurations.entrySet()) {if (entry.getKey().startsWith("default.")) {for (Class<?> configuration : entry.getValue().getConfiguration()) {context.register(configuration);}}}//defaultConfigType就是LoadBalancerClientConfiguration,在子类LoadBalancerClientFactory的构造方法传入//在刚才分析LoadBalancerClientFactory的时候介绍过context.register(PropertyPlaceholderAutoConfiguration.class, this.defaultConfigType);context.getEnvironment().getPropertySources().addFirst(new MapPropertySource(this.propertySourceName,Collections.<String, Object>singletonMap(this.propertyName, name)));//将父容器添加进去if (this.parent != null) {// Uses Environment from parent as well as beanscontext.setParent(this.parent);}context.setDisplayName(generateDisplayName(name));context.refresh();return context;}protected String generateDisplayName(String name) {return this.getClass().getSimpleName() + "-" + name;}/*** 每个LoadBalancerClientSpecification都创建一个AnnotationConfigApplicationContext* 也就是每个LoadBalancerClient会对应一个容器,其中的配置就对应容器中bean实例* */public <T> T getInstance(String name, Class<T> type) {//获取AnnotationConfigApplicationContext类型的容器AnnotationConfigApplicationContext context = getContext(name);try {//从容器中获取对应的实例return context.getBean(type);}catch (NoSuchBeanDefinitionException e) {// ignore}return null;}public <T> ObjectProvider<T> getLazyProvider(String name, Class<T> type) {return new ClientFactoryObjectProvider<>(this, name, type);}public <T> ObjectProvider<T> getProvider(String name, Class<T> type) {AnnotationConfigApplicationContext context = getContext(name);return context.getBeanProvider(type);}public <T> T getInstance(String name, Class<?> clazz, Class<?>... generics) {ResolvableType type = ResolvableType.forClassWithGenerics(clazz, generics);return getInstance(name, type);}@SuppressWarnings("unchecked")public <T> T getInstance(String name, ResolvableType type) {AnnotationConfigApplicationContext context = getContext(name);String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context, type);if (beanNames.length > 0) {for (String beanName : beanNames) {if (context.isTypeMatch(beanName, type)) {return (T) context.getBean(beanName);}}}return null;}public <T> Map<String, T> getInstances(String name, Class<T> type) {AnnotationConfigApplicationContext context = getContext(name);return BeanFactoryUtils.beansOfTypeIncludingAncestors(context, type);}/*** Specification with name and configuration.*/public interface Specification {String getName();Class<?>[] getConfiguration();}}

到这里将每个@LoadBalancerClient都创建了AnnotationConfigApplicationContext的容器,然后放到了 LoadBalancerClientFactory

到这里bean的生成过程分析完毕,下一篇文章会分析整个执行过程

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

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

相关文章

Mongodb:业务应用(1)

环境搭建参考&#xff1a;mongodb&#xff1a;环境搭建_Success___的博客-CSDN博客 需求&#xff1a; 在文章搜索服务中实现保存搜索记录到mongdb 并在搜索时查询出mongdb保存的数据 1、安装mongodb依赖 <dependency><groupId>org.springframework.data</groupI…

Intellij IDEA 导入 eclipse web 项目详细操作

Eclipse当中的web项目都会有这两个文件。但是idea当中应该是没有的&#xff0c;所以导入会出现兼容问题。但是本篇文章会教大家如何导入&#xff0c;并且导入过后还能使用tomcat运行。文章尽可能以图片的形式进行演示。我的idea使用的版本是2022.3.3版本。当然按正常来说版本之…

查询树形目录(内存遍历成树返回)

实体 Data TableName("dtp_sm_servicetype") ApiModel(value "SmServicetype对象", description "服务类型") EqualsAndHashCode(callSuper true) public class SmServicetype extends BaseEntity {ApiModelProperty("服务类型名称&quo…

C++ ModBUS TCP客户端工具 qModMaster 介绍及使用

qModMaster工具介绍 QModMaster是一个基于Qt的Modbus主站&#xff08;Master&#xff09;模拟器&#xff0c;用于模拟和测试Modbus TCP和RTU通信。它提供了一个直观的图形界面&#xff0c;使用户能够轻松设置和发送Modbus请求&#xff0c;并查看和分析响应数据。 以下是QModM…

图论——最短路算法

引入&#xff1a; 如上图&#xff0c;已知图G。 问节点1到节点3的最短距离。 可心算而出为d[1,2]d[2,3]112,比d[1,3]要小。 求最短路径算法&#xff1a; 1.Floyd(弗洛伊德) 是一种基于三角形不等式的多源最短路径算法。边权可以为负数 表现为a[i,j]a[j,k]<a[i,k]。 …

vue3 使用 element-china-area-data 实现地区选择器

官方地址&#xff1a;https://www.npmjs.com/package/element-china-area-data?activeTabreadme 在线示例&#xff1a;https://plortinus.github.io/element-china-area-data/index.html 实际使用 <el-col :span"12"><el-form-item label"所处城市&…

什么是响应式设计?列举几种实现响应式设计的方法。

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 什么是响应式设计&#xff1f;⭐ 实现响应式设计的方法⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅&#xff01;这个专栏…

Docker安装ElasticSearch/ES 7.4.0

目录 前言安装ElasticSearch/ES安装步骤1&#xff1a;准备1. 安装docker2. 搜索可以使用的镜像。3. 也可从docker hub上搜索镜像。4. 选择合适的redis镜像。 安装步骤2&#xff1a;拉取ElasticSearch镜像1 拉取镜像2 查看已拉取的镜像 安装步骤3&#xff1a;创建容器创建容器方…

android 如何分析应用的内存(十八)终章——使用Perfetto查看内存与调用栈之间的泄露

android 如何分析应用的内存&#xff08;十八&#xff09; 在前面两篇文章中&#xff0c;先是介绍了如何用AS查看Android的堆内存&#xff0c;然后介绍了使用MAT查看 Android的堆内存。AS能够满足基本的内存分析需求&#xff0c;但是无法进行多个堆的综合比较&#xff0c;因此…

ArcGIS Pro基础:【按顺序编号】工具实现属性字段的编号自动赋值

本次介绍一个字段的自动排序编号赋值工具&#xff0c;基于arcgis 的字段计算器工具也可以实现类似功能&#xff0c;但是需要自己写一段代码实现&#xff0c; 相对而言不是很方便。 如下所示&#xff0c;该工具就是【编辑】下的【属性】下的【按顺序编号】工具。 其操作方法是…

FreeRTOS(二值信号量)

资料来源于硬件家园&#xff1a;资料汇总 - FreeRTOS实时操作系统课程(多任务管理) 目录 一、信号量的概念 1、信号量的基本概念 2、信号量的分类 二、二值信号量的定义与应用 1、二值信号量的定义 2、二值信号量的应用 三、二值信号量的运作机制 1、FreeRTOS任务间二值…

可视化绘图技巧100篇基础篇(二)-柱状图

目录 前言 什么是柱状图 柱状图的组成 使用场景 适用场景: 不适用场景:

Spring Web

◆ Spring整合web环境 - Javaweb三大组件及环境特点 - Spring整合web环境的思路及实现 把ApplicationContext放在ServleContent域【listen组件中】中 ContextLoaderListener &#xff1a;部分代码写死了 /*** 配置通用的Spring容器的创建&#xff0c;只需要创建一次就可以*/…

hackrf-faq-1

Getting Help — HackRF documentation HackRF Product Line - Great Scott Gadgets Software Defined Radio with HackRF - Great Scott Gadgets What is the Transmit Power of HackRF? HackRF One’s absolute maximum TX power varies by operating frequency: 1 MHz t…

SQL server 与 MySQL count函数、以及sum、avg 是否包含 为null的值

sql server 与 mysql count 作用一样。 count 计算指定字段出现的个数&#xff0c; 不是计算 null的值 获取表的条数 count(n) n:常数 count(1),count&#xff08;0&#xff09;等 count(*) count(字段) 其中字段为null 不会统计在内。 avg(字段)、sum(字段) 跟count(字段)…

科技资讯|苹果手机版Vision Pro头显专利曝光,内嵌苹果手机使用

根据美国商标和专利局&#xff08;USPTO&#xff09;公示的清单&#xff0c;苹果公司近日获得了一项头显相关的技术专利&#xff0c;展示了一款亲民款 Vision Pro 头显&#xff0c;可以将 iPhone 放置在头显内部充当屏幕。 根据patentlyapple 媒体报道&#xff0c;这是苹果公司…

案例12 Spring MVC入门案例

网页输入http://localhost:8080/hello&#xff0c;浏览器展示“Hello Spring MVC”。 1. 创建项目 选择Maven快速构建web项目&#xff0c;项目名称为case12-springmvc01。 2.配置Maven依赖 <?xml version"1.0" encoding"UTF-8"?><project xm…

【计算机网络】TCP协议超详细讲解

文章目录 1. TCP简介2. TCP和UDP的区别3. TCP的报文格式4. 确认应答机制5. 超时重传6. 三次握手7. 为什么两次握手不行?8. 四次挥手9. 滑动窗口10. 流量控制11. 拥塞控制12. 延时应答13. 捎带应答14. 面向字节流15. TCP的连接异常处理 1. TCP简介 TCP协议广泛应用于可靠性要求…

Mybatis动态SQL,标签大全

动态SQL常用场景 批量删除delete from t_car where id in(1,2,3,4,5,6,......这里的值是动态的&#xff0c;根据用户选择的 id不同&#xff0c;值是不同的);多条件查询哪些字段会作为查询条件是不确定的&#xff0c;根据用户而定 select * from 1 t_car where brand like 丰田…

IP 协议的相关特性和数据链路层相关知识总结

目录 IP 协议的相关特性 一、IP协议的特性 二、 IP协议数据报格式 三、 IP协议的主要功能 1. 地址管理 动态分配 IP地址 NAT机制 NAT背景下的通信 IPV6 2. 路由控制​​​​​​​ 3.IP报文的分片与重组 数据链路层相关知识 1、以太网协议&#xff08;Ethernet&#xff09; 2.M…