@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自动就有了负载均衡能力。

相关推荐
z_mazin18 小时前
逆向一种私有二进制序列化格式:从零到字节级解析器
网络·python·网络协议·算法·rpc
欢醉3 天前
生产复盘:请求报413 Request Entity Too Large问题分享
springboot·springcloud
++==4 天前
RPC:grpc的使用与安装、server和client的使用示例、lsb_relese、自定制协议(LVC)+protobuf的RPC调用框架的实现
网络·网络协议·rpc
码匠许师傅5 天前
【设计模式精讲】21.迭代器模式(Iterator)
c++·设计模式·rpc·迭代器模式·软件工程·uml
天天喝旺仔5 天前
gRPC 流式通信实战:从一元调用到双向流,吃透四种 RPC 模式(Go 版)
分布式·http·微服务·rpc·go
苏生Susheng7 天前
【软件实施】Linux企业运维常用命令手册
java·linux·运维·服务器·springboot·springcloud·软件实施
可爱系程序猿8 天前
添加网络打印机提示 0x000006ba:从 RPC 服务、Print Spooler 到主机发现排查
网络·网络协议·rpc
可爱系程序猿9 天前
Windows 11 添加共享打印机提示 0x00000bc4:从 RPC 发现到策略配置排查
windows·网络协议·rpc
Patrick_Wilson13 天前
RPC 与 REST 的本质区别看这一篇就够了:别再只看 URL 里有没有动词
http·rpc·restful
catino13 天前
spring-IOC、DI
java·spring·rpc