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

相关推荐
☆致夏☆10 小时前
RPC通信原理实战
网络·网络协议·rpc
doglc13 小时前
从零手写 RPC-version1
java·网络·网络协议·rpc
doglc1 天前
Java 动态代理教程(JDK 动态代理)(以RPC 过程为例)
java·rpc·动态代理·jdk动态代理
BillKu1 天前
传入的表格格式数据流(TDS)远程过程调用(RPC)协议流不正确。此 RPC 请求中提供了过多的参数。最多应为 2100。
网络·网络协议·rpc
听雨·眠1 天前
关于RPC
网络协议·rpc
左灯右行的爱情3 天前
应用层核心协议详解:HTTP, HTTPS, RPC 与 Nginx
http·rpc·https
shangxianjiao6 天前
Javaweb后端 maven高级 maven聚合
java·maven·springboot·springcloud·聚合
doglc7 天前
从零手写RPC-version0
java·git·rpc·maven·intellij idea
朴拙数科8 天前
MCP(模型上下文协议)、A2A(Agent2Agent)协议和JSON-RPC 2.0的前沿技术解析
网络协议·rpc·json
23级二本计科9 天前
C++ Json-Rpc框架-3项目实现(2)
服务器·开发语言·c++·rpc