@LoadBalanced注解的实现原理

devtools/2025/1/20 14:32:02/
  • @LoadBalanced

@LoadBalanced注解通常结合RestTemplate使用,RestTemplateSpringCloud提供的一个编程式的实现远程过程调用的组件,简单来说就是可以实现发送http请求。但是在基于服务发现发送请求时,RestTemplate自己无法实现负载均衡,通常要标注@LoadBalanced。
虽然之后一个RestTemplate对象,但这个对象是线程安全的,多个线程在共同使用这个对象时不会有线程安全问题。

    @LoadBalanced@Beanpublic RestTemplate restTemplate(){return new RestTemplate();}
  • 注解实现原理
    @LoadBalanced注解是spring-cloud-starter-loadbalancer依赖引入的,这个依赖引入了LoadBalancerAutoConfiguration。

简单看一下这个类的源码:

@Configuration(proxyBeanMethods = false
)
@Conditional({BlockingRestClassesPresentCondition.class})
@ConditionalOnBean({LoadBalancerClient.class})
@EnableConfigurationProperties({LoadBalancerClientsProperties.class})
public class LoadBalancerAutoConfiguration {@LoadBalanced@Autowired(required = false)private List<RestTemplate> restTemplates = Collections.emptyList();@Autowired(required = false)private List<LoadBalancerRequestTransformer> transformers = Collections.emptyList();public LoadBalancerAutoConfiguration() {}@Beanpublic SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {return () -> restTemplateCustomizers.ifAvailable((customizers) -> {for(RestTemplate restTemplate : this.restTemplates) {for(RestTemplateCustomizer customizer : customizers) {customizer.customize(restTemplate);}}});}@Bean@ConditionalOnMissingBeanpublic LoadBalancerRequestFactory loadBalancerRequestFactory(LoadBalancerClient loadBalancerClient) {return new LoadBalancerRequestFactory(loadBalancerClient, this.transformers);}@Configuration(proxyBeanMethods = false)@Conditional({RetryMissingOrDisabledCondition.class})static class LoadBalancerInterceptorConfig {LoadBalancerInterceptorConfig() {}@Beanpublic LoadBalancerInterceptor loadBalancerInterceptor(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);};}@Bean@ConditionalOnBean({LoadBalancerInterceptor.class})@ConditionalOnMissingBeanLoadBalancerRestClientBuilderBeanPostProcessor lbRestClientPostProcessor(final LoadBalancerInterceptor loadBalancerInterceptor, ApplicationContext context) {return new LoadBalancerRestClientBuilderBeanPostProcessor(loadBalancerInterceptor, context);}}private static class RetryMissingOrDisabledCondition extends AnyNestedCondition {RetryMissingOrDisabledCondition() {super(ConfigurationPhase.REGISTER_BEAN);}@ConditionalOnMissingClass({"org.springframework.retry.support.RetryTemplate"})static class RetryTemplateMissing {RetryTemplateMissing() {}}@ConditionalOnProperty(value = {"spring.cloud.loadbalancer.retry.enabled"},havingValue = "false")static class RetryDisabled {RetryDisabled() {}}}@Configuration(proxyBeanMethods = false)@ConditionalOnClass({RetryTemplate.class})public static class RetryAutoConfiguration {public RetryAutoConfiguration() {}@Bean@ConditionalOnMissingBeanpublic LoadBalancedRetryFactory loadBalancedRetryFactory() {return new LoadBalancedRetryFactory() {};}}@Configuration(proxyBeanMethods = false)@ConditionalOnClass({RetryTemplate.class})@ConditionalOnBean({ReactiveLoadBalancer.Factory.class})@ConditionalOnProperty(value = {"spring.cloud.loadbalancer.retry.enabled"},matchIfMissing = true)public static class RetryInterceptorAutoConfiguration {public RetryInterceptorAutoConfiguration() {}@Bean@ConditionalOnMissingBeanpublic RetryLoadBalancerInterceptor loadBalancerInterceptor(LoadBalancerClient loadBalancerClient, LoadBalancerRequestFactory requestFactory, LoadBalancedRetryFactory loadBalancedRetryFactory, ReactiveLoadBalancer.Factory<ServiceInstance> loadBalancerFactory) {return new RetryLoadBalancerInterceptor(loadBalancerClient, requestFactory, loadBalancedRetryFactory, loadBalancerFactory);}@Bean@ConditionalOnMissingBeanpublic RestTemplateCustomizer restTemplateCustomizer(final RetryLoadBalancerInterceptor loadBalancerInterceptor) {return (restTemplate) -> {List<ClientHttpRequestInterceptor> list = new ArrayList(restTemplate.getInterceptors());list.add(loadBalancerInterceptor);restTemplate.setInterceptors(list);};}@Bean@ConditionalOnBean({RetryLoadBalancerInterceptor.class})@ConditionalOnMissingBeanLoadBalancerRestClientBuilderBeanPostProcessor lbRestClientPostProcessor(final RetryLoadBalancerInterceptor loadBalancerInterceptor, ApplicationContext context) {return new LoadBalancerRestClientBuilderBeanPostProcessor(loadBalancerInterceptor, context);}}
}

核心实现原理:

  1. 从容器中拿到所有标注了@LoadBalanced注解的Bean,所以没有标注 @LoadBalanced注解的RestTemplate是无法实现负载均衡的。
    @LoadBalanced@Autowired(required = false)private List<RestTemplate> restTemplates = Collections.emptyList();
  1. 拿到这些Bean对象之后,给这些Bean对象设置请求拦截器
    @Beanpublic SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {return () -> restTemplateCustomizers.ifAvailable((customizers) -> {for(RestTemplate restTemplate : this.restTemplates) {for(RestTemplateCustomizer customizer : customizers) {customizer.customize(restTemplate);}}});}

这里就是调用了customizer.customize(restTemplate) 来为每一个RestTemplate添加拦截器。

RestTemplateCustomizer 是一个函数式接口,同样在LoadBalancerAutoConfiguration进行了实现

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

很明显这里就是使用lambda表达式定义了一个RestTemplateCustomizer 的实现。而这个方法就是容器中获取LoadBalancerInterceptor,然后获取restTemplate已有的拦截器,并且加入新的拦截器再重新赋值。

  • LoadBalancerInterceptor

看一下LoadBalancerInterceptor的实现,这个是实现负载均衡的关键

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

很明显可以看到intercept中使用了LoadBalancerClient进行了服务负载均衡。

简而言之就是LoadBalancerAutoConfiguration这个类获取到了容器中所有的RestTemplate组件,并添加了负载均衡拦截器,这样@Bean的RestTemplate自动就有了负载均衡能力。


http://www.ppmy.cn/devtools/152112.html

相关文章

DEBERTA:具有解耦注意力机制的解码增强型BERT

摘要 近年来&#xff0c;预训练神经语言模型的进展显著提升了许多自然语言处理&#xff08;NLP&#xff09;任务的性能。本文提出了一种新的模型架构DeBERTa&#xff08;具有解耦注意力机制的解码增强型BERT&#xff09;&#xff0c;通过两种新技术改进了BERT和RoBERTa模型。第…

基于Springboot+Redis秒杀系统 demo

基于SpringBootRedis的商品秒杀系统的Demo。这个例子将展示如何防止商品超卖。 首先创建项目依赖(pom.xml): <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>…

【Hadoop面试题2025】

文章目录 简单题故障及相应的处理方法中等难度高难度小文件小文件的产生小文件问题的影响小文件治理方案推荐方案 冷文件冷文件的产生冷文件问题的影响冷文件治理方案推荐方案 简单题 一、基础概念类 什么是Hadoop&#xff1f; 答案&#xff1a;Hadoop是一个开源的分布式计算框…

如何使用 useMemo 和 memo 优化 React 应用性能?

使用 useMemo 和 memo 优化 React 应用性能 在构建复杂的 React 应用时&#xff0c;性能优化是确保应用流畅运行的关键。React 提供了多种工具来帮助开发者优化组件的渲染和计算逻辑&#xff0c;其中 useMemo 和 memo 是两个非常有用的 Hook。本文将详细介绍这两个工具的使用方…

【Kotlin】上手学习之类型篇

一、类型 1.1 基本类型 主要分为 数字及其无符号版布尔字符字符串数组 1.1.1 数字 整数类型 Kotlin 提供了一组表示数字的内置类型。 对于整数&#xff0c;有四种不同大小的类型&#xff0c;因此值的范围也不同&#xff1a; 类型大小&#xff08;比特数&#xff09;最小…

Postman环境变量全局变量设置

在公司中&#xff0c;一般会存在开发环境、测试环境、线上环境等&#xff0c;如果需要在不 同的环境下切换做接口测试&#xff0c;显然我们需要把所有接口的域名进行修改&#xff0c;如果接 口测试用例较多&#xff0c;那么修改会非常费力&#xff0c;postman可直接通过切换环境…

数据库高可用方案-04-删除策略

数据库数据高可用系列 数据库高可用方案-01-数据库备份还原方案 数据库高可用方案-02-多机房部署 数据库高可用方案-03-主备等高可用架构 数据库高可用方案-04-删除策略 数据库高可用方案-05-备份与恢复 数据库高可用方案-06-监控与报警 数据库高可用方案-07-一致性校验…

【Spring Boot】掌握 Spring 事务:隔离级别与传播机制解读与应用

前言 &#x1f31f;&#x1f31f;本期讲解关于spring 事务传播机制介绍~~~ &#x1f308;感兴趣的小伙伴看一看小编主页&#xff1a;GGBondlctrl-CSDN博客 &#x1f525; 你的点赞就是小编不断更新的最大动力 &#x1f386;那么废话…