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

相关推荐
poemyang3 小时前
从C10K到Reactor:事件驱动,如何重塑高并发服务器的网络架构
rpc·reactor·事件驱动
linweidong1 天前
理想汽车Java后台开发面试题及参考答案(下)
jvm·spring boot·spring cloud·rpc·虚拟机·feign·二叉树排序
沐风ya2 天前
RPC介绍
网络·网络协议·rpc
余辉zmh2 天前
【C++篇】:ServiceBus RPC 分布式服务总线框架项目
开发语言·c++·rpc
poemyang3 天前
单线程如何撑起百万连接?I/O多路复用:现代网络架构的基石
linux·rpc·i/o 模式
poemyang4 天前
你的程序为何卡顿?从LINUX I/O三大模式寻找答案
linux·rpc
poemyang5 天前
“一切皆文件”:揭秘LINUX I/O与虚拟内存的底层设计哲学
linux·rpc·i/o 模式
jz_ddk5 天前
[LVGL] 从0开始,学LVGL:基础构建篇 - 掌握UI的核心构建块
linux·网络协议·ui·rpc·嵌入式·gui·lvgl
jc06205 天前
4.4-中间件之gRPC
c++·中间件·rpc
土星碎冰机6 天前
Dubbo RPC 调用中用户上下文传递问题的解决
网络协议·rpc·dubbo