• 负载均衡组件Ribbon核心-@LoadBalanced-下


    引言

    书接上篇 负载均衡组件Ribbon核心-@LoadBalanced-上 我们讲完了理解@LoadBalanced注解的知识准备,接下来就是@LoadBalanced注解详解。

    LoadBalancerAutoConfiguration 激活

    @LoadBalanced 注解功能起点来至LoadBalancerAutoConfiguration自动配置类,SpringBoot容器启动的时,根据加入的依赖(spring-web/spring-cloud-ribbon),LoadBalancerAutoConfiguration 配置类会被激活

    1. @Configuration(proxyBeanMethods = false)
    2. @ConditionalOnClass(RestTemplate.class)
    3. @ConditionalOnBean(LoadBalancerClient.class)
    4. @EnableConfigurationProperties(LoadBalancerRetryProperties.class)
    5. public class LoadBalancerAutoConfiguration {
    6. }

    激活条件: 

    @ConditionalOnClass(RestTemplate.class):spring-web 依赖引入RestTemplate类
    @ConditionalOnBean(LoadBalancerClient.class):要求容器内有LoadBalancerClient Bean实例

    这是,肯定有朋友疑惑:@LoadBalanced功能起点不是这个配置类么,哪来LoadBalancerClient Bean实例。有疑惑是对的,@LoadBalanced 注解主宰的是Ribbon负载均衡功能,而不是整个Ribbon操作,LoadBalancerClient Bean 在Ribbon 加载前就已经配置被创建了。且看

     这个就是Ribbon组件引入的启动器,里面有个Ribbion自动配置类:RibbonAutoConfiguration,看下源码

    1. @Configuration
    2. @Conditional(RibbonAutoConfiguration.RibbonClassesConditions.class)
    3. @RibbonClients
    4. @AutoConfigureAfter(
    5. name = "org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration")
    6. @AutoConfigureBefore({ LoadBalancerAutoConfiguration.class,
    7. AsyncLoadBalancerAutoConfiguration.class })
    8. @EnableConfigurationProperties({ RibbonEagerLoadProperties.class,
    9. ServerIntrospectorProperties.class })
    10. public class RibbonAutoConfiguration {
    11. @Bean
    12. @ConditionalOnMissingBean(LoadBalancerClient.class)
    13. public LoadBalancerClient loadBalancerClient() {
    14. return new RibbonLoadBalancerClient(springClientFactory());
    15. }
    16. }

    再看头顶贴上的注解:

    1. @AutoConfigureBefore({ LoadBalancerAutoConfiguration.class,
    2. AsyncLoadBalancerAutoConfiguration.class })

    真相来了,RibbonAutoConfiguration 配置类的启动早于LoadBalancerAutoConfiguration,也就是说,RibbonAutoConfiguration 配置的LoadBalancerClient  实例在LoadBalancerAutoConfiguration启动前就已经实例化好了。

    RestTemplate 负载均衡能力集成

    当了解了LoadBalancerAutoConfiguration配置类如何进行激活,接下就看如何将负载均衡的能力集成在RestTemplate实例中。总结一下需要走下面3步:

    1>RestTemplate 收集  2>RestTemplate 定制器   3>RestTemplate拦截注入

    步骤1:RestTemplate 收集

    我们从LoadBalancerAutoConfiguration配置类restTemplates集合属性发现端倪。

    1. public class LoadBalancerAutoConfiguration {
    2. @LoadBalanced
    3. @Autowired(required = false)
    4. private List restTemplates = Collections.emptyList();
    5. ...
    6. }

    从源码上看,Spring容器会收集所有RestTemplate实例,并注入restTemplates 中。此时问题来了,它怎么知道哪些RestTemplate 类需要添加负载均衡功能?这是就用到@Qualifier 知识点了(不了解朋友可以看上篇)。

    先看@LoadBalanced注解的源码

    1. @Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
    2. @Retention(RetentionPolicy.RUNTIME)
    3. @Documented
    4. @Inherited
    5. @Qualifier
    6. public @interface LoadBalanced {
    7. }

    发现它头上有个元注解:@Qualifier, 意味着@LoadBalanced 拥有@Qualifier 注解所有功能,

    1. @LoadBalanced
    2. @Autowired(required = false)
    3. private List restTemplates = Collections.emptyList();
    4. -------------------------------------------------------------------------------
    5. //@LoadBalanced
    6. @Qualifier //等价
    7. @Autowired(required = false)
    8. private List restTemplates = Collections.emptyList();

    很明显,它用上了@Qualifier 注解 Bean筛选标记功能。

    步骤2:RestTemplate 定制器

    步骤1完成后,可以得到待加负载均衡逻辑的restTemplates 集合。接下来就是集成了,Spring提供了XxxCustomizer类实现实例定制,下面是RestTemplate定制器接口源码

    1. public interface RestTemplateCustomizer {
    2. void customize(RestTemplate restTemplate);
    3. }

    接口只有一个方法:customize, 参数为RestTemplate,很明显看出它就用来改造RestTemplate 实例的。

    继续往下,LoadBalancerAutoConfiguration配置类下有个LoadBalancerInterceptorConfig 静态内部类

    1. public class LoadBalancerAutoConfiguration {
    2. @Configuration(proxyBeanMethods = false)
    3. @ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
    4. static class LoadBalancerInterceptorConfig {
    5. ....
    6. @Bean
    7. @ConditionalOnMissingBean
    8. public RestTemplateCustomizer restTemplateCustomizer(
    9. final LoadBalancerInterceptor loadBalancerInterceptor) {
    10. return restTemplate -> {
    11. List list = new ArrayList<>(
    12. restTemplate.getInterceptors());
    13. list.add(loadBalancerInterceptor);
    14. restTemplate.setInterceptors(list);
    15. };
    16. }
    17. }
    18. }
    19. }

    类内部,定义了用于构建RestTemplateCustomizer 定制器实例方法:restTemplateCustomizer(loadBalancerInterceptor),参数是loadBalancerInterceptor 拦截器。后续将该拦截器添加到restTemplate 实例中。

    1. list.add(loadBalancerInterceptor);
    2. restTemplate.setInterceptors(list);

    定制逻辑完成,接下来回答2个问题,RestTemplate 类负载均衡逻辑就通了

    问题1:RestTemplate 定制器怎么执行的?

    问题2:LoadBalancerInterceptor 拦截器做啥逻辑?

    这里先回答问题1,问题2在步骤3详解

    RestTemplate 定制器使用 SmartInitializingSingleton 接口 (不理解/忘记可以看上篇)实现调用。

    当Spring容器所有单例Bean实例初始化成功之后,实现SmartInitializingSingleton 接口Bean实例afterSingletonsInstantiated 方法会被回调。

    1. public class LoadBalancerAutoConfiguration {
    2. ....
    3. @Bean
    4. public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(
    5. final ObjectProvider> restTemplateCustomizers) {
    6. return () -> restTemplateCustomizers.ifAvailable(customizers -> {
    7. for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
    8. for (RestTemplateCustomizer customizer : customizers) {
    9. customizer.customize(restTemplate);
    10. }
    11. }
    12. });
    13. }
    14. ....
    15. }

    源码逻辑:收集容器中所有RestTemplateCustomizer  定制器,对步骤1收集到的LoadBalancerAutoConfiguration.this.restTemplates RestTemplate对象集合,一一定制。

    步骤3:RestTemplate 拦截器

    还是LoadBalancerAutoConfiguration配置类下有个LoadBalancerInterceptorConfig 静态内部类

    1. public class LoadBalancerAutoConfiguration {
    2. @Configuration(proxyBeanMethods = false)
    3. @ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
    4. static class LoadBalancerInterceptorConfig {
    5. @Bean
    6. public LoadBalancerInterceptor ribbonInterceptor(
    7. LoadBalancerClient loadBalancerClient,
    8. LoadBalancerRequestFactory requestFactory) {
    9. return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
    10. }
    11. ....
    12. }
    13. }
    14. }

    类内部,定义ribbonInterceptor 实例方法,参数为loadBalancerClient,requestFactory ,目的地是创建LoadBalancerInterceptor 拦截器实例。

    步骤2中问题:LoadBalancerInterceptor 拦截器做了啥操作?

    上篇文章我们了解过ClientHttpRequestInterceptor拦截器,当RestTemplate 调用getForEntity/getForObject方法发起http请求时,会执行拦截逻辑,LoadBalancerInterceptor  拦截器就是ClientHttpRequestInterceptor 实现类

    1. public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {
    2. ....
    3. @Override
    4. public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
    5. final ClientHttpRequestExecution execution) throws IOException {
    6. final URI originalUri = request.getURI();
    7. String serviceName = originalUri.getHost();
    8. Assert.state(serviceName != null,
    9. "Request URI does not contain a valid hostname: " + originalUri);
    10. return this.loadBalancer.execute(serviceName,
    11. this.requestFactory.createRequest(request, body, execution));
    12. }
    13. }

    大白话结论

    综上3个步骤,得出结论:

    当贴有@LoadBalanced 注解的RestTemplate发起getForEntity/getForObject 会执行RestTemplateCustomizer 定制器绑定LoadBalancerInterceptor 拦截器 intercept 方法,然后返回ClientHttpResponse 结果

    负载均衡后续

    到这,肯定有好学的小伙伴问,最后一步的负载均衡怎么调用啊?这个问题已经超出本篇文章的讨论了,如果想深究的朋友,还可以继续:

    1. @Override
    2. public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
    3. final ClientHttpRequestExecution execution) throws IOException {
    4. final URI originalUri = request.getURI();
    5. String serviceName = originalUri.getHost();
    6. Assert.state(serviceName != null,
    7. "Request URI does not contain a valid hostname: " + originalUri);
    8. return this.loadBalancer.execute(serviceName,
    9. this.requestFactory.createRequest(request, body, execution));
    10. }

    LoadBalancerInterceptor 拦截器 intercept 方法执行,调用了this.loadBalancer.execute 实现远程服务调用,负载均衡的调用效果,就由这个方法完成。再深入,就进到:

    1. public class RibbonLoadBalancerClient implements LoadBalancerClient {
    2. public T execute(String serviceId, LoadBalancerRequest request, Object hint)
    3. throws IOException {
    4. ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
    5. Server server = getServer(loadBalancer, hint);
    6. if (server == null) {
    7. throw new IllegalStateException("No instances available for " + serviceId);
    8. }
    9. RibbonServer ribbonServer = new RibbonServer(serviceId, server,
    10. isSecure(server, serviceId),
    11. serverIntrospector(serviceId).getMetadata(server));
    12. return execute(serviceId, ribbonServer, request);
    13. }
    14. }

    最核心逻辑:

    Server server = getServer(loadBalancer, hint);
    1. protected Server getServer(ILoadBalancer loadBalancer, Object hint) {
    2. if (loadBalancer == null) {
    3. return null;
    4. }
    5. // Use 'default' on a null hint, or just pass it on?
    6. return loadBalancer.chooseServer(hint != null ? hint : "default");
    7. }

    这里的chooseServer方法,就是根据Ribbon配置的负载均衡策略来调用服务了,默认是

    1. public class ZoneAwareLoadBalancerextends Server> extends DynamicServerListLoadBalancer {
    2. public Server chooseServer(Object key) {
    3. ...
    4. }
    5. }

    好了,到这,就真的结束了~

     看文字不过瘾可以切换视频版:SpringCloud Alibaba 极简入门

  • 相关阅读:
    HSTS(HTTP 严格传输安全)
    数组的内置功能
    C# 图解教程 第5版 —— 第8章 类和继承
    onlyoffice的介绍搭建、集成过程。Windows、Linux
    Hadoop3 - MapReduce 属性优化
    DMPE-PEG-Mal 二肉豆蔻酰磷脂酰乙醇胺-聚乙二醇-马来酰亚胺供应
    星起航:亚马逊助力卖家建设品牌形象,形成品牌效应促稳步增长
    【JY】求?减隔震元件的滞回面积~
    Python作业【简单算法题】
    FROZEN TRANSFORMERS IN LANGUAGE MODELS ARE EFFECTIVE VISUAL ENCODER LAYERS
  • 原文地址:https://blog.csdn.net/langfeiyes/article/details/128131908