@LoadBalanced注解的实现原理

  • @LoadBalanced

@LoadBalanced注解通常结合RestTemplate使用,RestTemplate是SpringCloud提供的一个编程式的实现远程过程调用的组件,简单来说就是可以实现发送http请求。但是在基于服务发现发送请求时,RestTemplate自己无法实现负载均衡,通常要标注@LoadBalanced。

虽然之后一个RestTemplate对象,但这个对象是线程安全的,多个线程在共同使用这个对象时不会有线程安全问题。

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

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

java 复制代码
@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() {
    }

    @Bean
    public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {
        return () -> restTemplateCustomizers.ifAvailable((customizers) -> {
                for(RestTemplate restTemplate : this.restTemplates) {
                    for(RestTemplateCustomizer customizer : customizers) {
                        customizer.customize(restTemplate);
                    }
                }

            });
    }

    @Bean
    @ConditionalOnMissingBean
    public LoadBalancerRequestFactory loadBalancerRequestFactory(LoadBalancerClient loadBalancerClient) {
        return new LoadBalancerRequestFactory(loadBalancerClient, this.transformers);
    }

    @Configuration(
        proxyBeanMethods = false
    )
    @Conditional({RetryMissingOrDisabledCondition.class})
    static class LoadBalancerInterceptorConfig {
        LoadBalancerInterceptorConfig() {
        }

        @Bean
        public LoadBalancerInterceptor loadBalancerInterceptor(LoadBalancerClient loadBalancerClient, LoadBalancerRequestFactory requestFactory) {
            return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
        }

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

        @Bean
        @ConditionalOnBean({LoadBalancerInterceptor.class})
        @ConditionalOnMissingBean
        LoadBalancerRestClientBuilderBeanPostProcessor 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
        @ConditionalOnMissingBean
        public 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
        @ConditionalOnMissingBean
        public RetryLoadBalancerInterceptor loadBalancerInterceptor(LoadBalancerClient loadBalancerClient, LoadBalancerRequestFactory requestFactory, LoadBalancedRetryFactory loadBalancedRetryFactory, ReactiveLoadBalancer.Factory<ServiceInstance> loadBalancerFactory) {
            return new RetryLoadBalancerInterceptor(loadBalancerClient, requestFactory, loadBalancedRetryFactory, loadBalancerFactory);
        }

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

        @Bean
        @ConditionalOnBean({RetryLoadBalancerInterceptor.class})
        @ConditionalOnMissingBean
        LoadBalancerRestClientBuilderBeanPostProcessor lbRestClientPostProcessor(final RetryLoadBalancerInterceptor loadBalancerInterceptor, ApplicationContext context) {
            return new LoadBalancerRestClientBuilderBeanPostProcessor(loadBalancerInterceptor, context);
        }
    }
}

核心实现原理:

  1. 从容器中拿到所有标注了@LoadBalanced注解的Bean,所以没有标注 @LoadBalanced注解的RestTemplate是无法实现负载均衡的。
java 复制代码
    @LoadBalanced
    @Autowired(
        required = false
    )
    private List<RestTemplate> restTemplates = Collections.emptyList();
  1. 拿到这些Bean对象之后,给这些Bean对象设置请求拦截器
java 复制代码
    @Bean
    public 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进行了实现

java 复制代码
public interface RestTemplateCustomizer {
    void customize(RestTemplate restTemplate);
}
java 复制代码
        @Bean
        @ConditionalOnMissingBean
        public 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的实现,这个是实现负载均衡的关键

java 复制代码
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自动就有了负载均衡能力。

相关推荐
better_liang9 小时前
每日Java面试场景题知识点之-分布式事务处理
java·微服务·面试·springcloud·分布式事务
q***017712 小时前
spring loC&DI 详解
java·spring·rpc
q***96582 天前
Spring总结(上)
java·spring·rpc
K***43064 天前
三大框架-Spring
java·spring·rpc
岚天start5 天前
K8S环境中Containerd运行时占用文件定位清理
java·rpc·kubernetes
ruleslol5 天前
Spring Cloud Gateway 断言完全讲解
springcloud
whltaoin5 天前
【 Java微服务 】Spring Cloud Alibaba :Nacos 注册中心与配置中心全攻略(含服务发现、负载均衡与动态配置)
java·微服务·nacos·springcloud·注册中心·配置中心
a crazy day6 天前
Spring相关知识点【详细版】
java·spring·rpc
编啊编程啊程6 天前
【044】Dubbo3从0到1系列之ExecutorRepository接口详解
rpc·dubbo
ANGLAL7 天前
30.分布式事务:本地事务 + RPC 的“隐形炸弹”
分布式·网络协议·rpc