@LoadBalanced注解RestTemplate拥有负载均衡的能力

本文涉及的产品
应用型负载均衡 ALB,每月750个小时 15LCU
网络型负载均衡 NLB,每月750个小时 15LCU
简介: @LoadBalanced注解RestTemplate拥有负载均衡的能力

image.png

关联阅读(必读)


发送http请求(1):发送http请求的几种方式

发送http请求(2):RestTemplate发送http请求

Springcloud源码阅读4-Ribbon负载均衡(下)


回顾


当我在Ribbon的环境下使用RestTemplate发送请求时,通常我们会像下面这样注入一个restTemplate

@Autowired
@LoadBalanced
RestTemplate restTemplate;
复制代码

为啥我们注入一个带有注解@LoadBalanced标注RestTemplate,此RestTemplate就具有负载均衡的能力,原理是什么呢?

RestTemplate发送http请求一节讲过,RestTemplate可以添加拦截器,在发送请求前,先执行拦截器内容。

Ribbon负载均衡(下)一节讲过,(LoadBalancerClient)RibbonLoadBalancerClient 具有负载均衡的能力。

猜测@LoadBalanced注解应该就是把这两点结合在了一起。


LoadBalancerAutoConfiguration配置类


这个整合剂,就是LoadBalancerAutoConfiguration配置类,

@Configuration
@ConditionalOnClass(RestTemplate.class)
@ConditionalOnBean(LoadBalancerClient.class)
@EnableConfigurationProperties(LoadBalancerRetryProperties.class)
public class LoadBalancerAutoConfiguration {
  //注入所有使用@LoadBalanced的RestTemplate
  @LoadBalanced
  @Autowired(required = false)
  private List<RestTemplate> restTemplates = Collections.emptyList();
  // 对所有的RestTemplate使用RestTemplateCustomizer定制器进行统一处理
  @Bean
  public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(
      final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {
    return () -> restTemplateCustomizers.ifAvailable(customizers -> {
            for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
                for (RestTemplateCustomizer customizer : customizers) {
                    customizer.customize(restTemplate);
                }
            }
        });
  }
  @Autowired(required = false)
  private List<LoadBalancerRequestTransformer> transformers = Collections.emptyList();
  //LoadBalancerRequest 创建工厂
  @Bean
  @ConditionalOnMissingBean
  public LoadBalancerRequestFactory loadBalancerRequestFactory(
      LoadBalancerClient loadBalancerClient) {
    return new LoadBalancerRequestFactory(loadBalancerClient, transformers);
  }
  //LoadBalancerInterceptor  拦截器配置类
    @Configuration
  @ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
  static class LoadBalancerInterceptorConfig {
    // 负载均衡拦截器,看其参数传入的是loadBalancerClient
    @Bean
    public LoadBalancerInterceptor ribbonInterceptor(
        LoadBalancerClient loadBalancerClient,
        LoadBalancerRequestFactory requestFactory) {
      return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
    }
    // RestTemplate定制器
    @Bean
    @ConditionalOnMissingBean
    public RestTemplateCustomizer restTemplateCustomizer(
        final LoadBalancerInterceptor loadBalancerInterceptor) {
      return restTemplate -> {
                List<ClientHttpRequestInterceptor> list = new ArrayList<>(
                        restTemplate.getInterceptors());
                list.add(loadBalancerInterceptor);
                restTemplate.setInterceptors(list);
            };
    }
  }
  //省略在有RetryTemplate类情况下的分析。
}


@LoadBalanced

@LoadBalanced注解的作用是啥呢?看起源码,我们发现他自身也有一个注解@Qualifier

@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier
public @interface LoadBalanced {
}

@Qualifier 我们应该认识,当容器内存在多个同类型的Bean实现类时,我们可以通过@Qualifier("service1")注解来标识,具体引入哪个。

此处的@LoadBalanced 其实就是@Qualifier的一种特殊形态。 当我们使用@LoadBalanced 标注一个Bean定义时,在自动注入的地方,也使用@LoadBalanced 来自动注入对应的Bean

@LoadBalanced
    RestTemplate restTemplate(){
        return new RestTemplate();
    }

注入带有@LoadBalanced注解的RestTemplate

@LoadBalanced
@Autowired(required = false)
private List<RestTemplate> restTemplates = Collections.emptyList();


LoadBalancerInterceptor

负载均衡拦截器,有了这个拦截器,就可以设置到Restmplate上去,在请求发起之前做负载均衡操作,从多个候选应用中选择出一个。

public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {
  // LoadBalancerClient  负载均衡客户端
  private LoadBalancerClient loadBalancer;
  // 用于构建出一个Request
  private LoadBalancerRequestFactory requestFactory;
  ... // 省略构造函数(给这两个属性赋值)
  @Override
  public ClientHttpResponse intercept(final HttpRequest request, final byte[] body, final ClientHttpRequestExecution execution) throws IOException {
    final URI originalUri = request.getURI();
    String serviceName = originalUri.getHost();
    Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri);
    return this.loadBalancer.execute(serviceName,
     this.requestFactory.createRequest(request, body, execution));
  }
}

可以看出LoadBalancerInterceptor  会调用loadBalancer#execute()方法做负载均衡,获取一个服务地址出来。并传入一个回调。

LoadBalancerRequestFactory类
public LoadBalancerRequest<ClientHttpResponse> createRequest(final HttpRequest request,
  final byte[] body, final ClientHttpRequestExecution execution) {
  //回掉方法。
  return instance -> {
            HttpRequest serviceRequest = new ServiceRequestWrapper(request, instance, loadBalancer);
            if (transformers != null) {
                for (LoadBalancerRequestTransformer transformer : transformers) {
                    serviceRequest = transformer.transformRequest(serviceRequest, instance);
                }
            }
        return execution.execute(serviceRequest, body);
    };
}

当负载均衡选择出一个服务地址,apply回调方法,将当前HttpRequest 进行包装,重写其getURI()

@Override
  public URI getURI() {
    URI uri = this.loadBalancer.reconstructURI(
        this.instance, getRequest().getURI());
    return uri;
  }

这样当真正发起请求时,获取的URI就是已经被负载均衡处理过的URI了。


RestTemplateCustomizer

RestTemplate定制器,用来对RestTemplate进行定制化操作,

此配置类注册的定制器,是用来对RestTemplate 设置LoadBalancerInterceptor的。

// RestTemplate定制器
@Bean
@ConditionalOnMissingBean
public RestTemplateCustomizer restTemplateCustomizer(
    final LoadBalancerInterceptor loadBalancerInterceptor) {
    return restTemplate -> {
                List<ClientHttpRequestInterceptor> list = new ArrayList<>(
                        restTemplate.getInterceptors());
                // 给RestTemplate  添加负载均衡拦截器
                list.add(loadBalancerInterceptor);
                restTemplate.setInterceptors(list);
        };
}

当然我们可以配置其他的RestTemplateCustomizer 来对RestTemplate做其他定制化操作。

而这些RestTemplateCustomizer 定制器,定制操作。统一由配置类中注册的SmartInitializingSingleton触发。


SmartInitializingSingleton

实现该接口类,当所有单例 bean 都初始化完成以后, 容器会回调该接口的方法 afterSingletonsInstantiated。

此配置类中SmartInitializingSingleton 做的操作就是:对哪些RestTemplate,执行哪些定制化操作

// 对所有的RestTemplate使用RestTemplateCustomizer定制器进行统一处理
  @Bean
  public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(
      final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {
    return () -> restTemplateCustomizers.ifAvailable(customizers -> {
            for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
                for (RestTemplateCustomizer customizer : customizers) {
                    customizer.customize(restTemplate);
                }
            }
        });
  }

也就说:spring在初始化过程后期,回调此配置类注册的SmartInitializingSingleton的afterSingletonsInstantiated,把上下文中的所有RestTemplateCustomizer 获取到,循环调用每个定制器的customize方法执行定制操作。其中就包括把LoadBalancerInterceptor设置到RestTemplate中。

至此:RestTemplate 就具有了负载均衡的能力。


总结


有@LoadBalanced 注解的RestTemplate,会被容器注入一个LoadBalancerInterceptor拦截器,从而使RestTemplate 具有负载均衡的能力。

讲完这节,Ribbon负载均衡与RestTemplate发送请求之间的知识点就形成了闭环。


相关实践学习
SLB负载均衡实践
本场景通过使用阿里云负载均衡 SLB 以及对负载均衡 SLB 后端服务器 ECS 的权重进行修改,快速解决服务器响应速度慢的问题
负载均衡入门与产品使用指南
负载均衡(Server Load Balancer)是对多台云服务器进行流量分发的负载均衡服务,可以通过流量分发扩展应用系统对外的服务能力,通过消除单点故障提升应用系统的可用性。 本课程主要介绍负载均衡的相关技术以及阿里云负载均衡产品的使用方法。
相关文章
|
负载均衡 算法 Java
Spring Cloud Alibaba - 06 RestTemplate 实现自定义负载均衡算法
Spring Cloud Alibaba - 06 RestTemplate 实现自定义负载均衡算法
222 0
|
负载均衡 Java Nacos
restTemplate loadbalance 负载均衡使用demo 案例 原理以及全网最细源码解析
restTemplate loadbalance 负载均衡使用demo 案例 原理以及全网最细源码解析
restTemplate loadbalance 负载均衡使用demo 案例 原理以及全网最细源码解析
|
负载均衡 网络协议 算法
SpringCloud服务间通信方式(RestTemplate)及负载均衡(Ribbon)
🍅程序员小王的博客:程序员小王的博客 🍅 欢迎点赞 👍 收藏 ⭐留言 📝 🍅 如有编辑错误联系作者,如果有比较好的文章欢迎分享给我,我会取其精华去其糟粕 🍅java自学的学习路线:java自学的学习路线
444 0
SpringCloud服务间通信方式(RestTemplate)及负载均衡(Ribbon)
|
负载均衡 网络协议 Java
Spring Cloud 学习笔记04----服务消费者(RestTemplate+Ribbon(客户端负载均衡))
上一篇《Spring Cloud 学习笔记02----服务治理》我们讲到了服务的注册与发现,在微服务框架中,我们将服务拆分成一个个独立部署的子服务,这些服务通过http rest 来通讯的,Spring Cloud有两种服务调用方式,一种是 RestTemplate+Ribbon, 另外一种是Feign 。
157 0
Spring Cloud 学习笔记04----服务消费者(RestTemplate+Ribbon(客户端负载均衡))
|
负载均衡 网络协议 Cloud Native
Spring Cloud Alibaba:Nacos服务治理平台,服务注册、RestTemplate实现微服务之间访问,负载均衡访问
Nacos 提供了发现、配置和管理微服务能力,能快速实现动态服务发现、服务配置、服务元数据及流量管理。使用 Nacos 可以更敏捷和容易地构建、交付和管理微服务平台。 Nacos 是构建以“服务”为中心的现代应用架构 (例如微服务范式、云原生范式) 的服务基础设施。 本文用Nacos、RestTemplate实现微服务之间访问。
296 0
Spring Cloud Alibaba:Nacos服务治理平台,服务注册、RestTemplate实现微服务之间访问,负载均衡访问
|
负载均衡 Java 容器
【Spring Cloud】 RestTemplate基于Ribbon的负载均衡实现原理 上
都知道 RestTemplate 是基于 Ribbon实现的负载均衡,那么Ribbon又是如何做到的 ? 首先找到 spring-cloud-netflix-ribbon-2.1.0.RELEASE.jar 这个jar包 ## 1.自动配置创建RibbonAutoConfiguration ![image-20220510203440880](https://img-blog.csdnimg.cn/img_convert/f0a478e8490a71010810423dad12e169.png) 查看META-INF下的spring.factories文件
132 0
|
6月前
|
缓存 负载均衡 算法
解读 Nginx:构建高效反向代理和负载均衡的秘密
解读 Nginx:构建高效反向代理和负载均衡的秘密
123 2
|
5月前
|
负载均衡 算法 应用服务中间件
nginx自定义负载均衡及根据cpu运行自定义负载均衡
nginx自定义负载均衡及根据cpu运行自定义负载均衡
94 1
|
5月前
|
运维 负载均衡 算法
SLB与NGINX的异同是什么
SLB与NGINX的异同是什么
473 2
|
7月前
|
负载均衡 应用服务中间件 nginx
解决nginx配置负载均衡时invalid host in upstream报错
在Windows环境下,配置Nginx 1.11.5进行负载均衡时遇到问题,服务无法启动。错误日志显示“invalid host in upstream”。检查发现上游服务器列表中,192.168.29.128的主机地址无效。负载均衡配置中,两个服务器地址前误加了&quot;http://&quot;。修正方法是删除上游服务器列表和proxy_pass中的&quot;http://&quot;。问题解决后,Nginx服务应能正常启动。
521 4
解决nginx配置负载均衡时invalid host in upstream报错