前提

在阅读这篇博客之前,希望你对SpringCloud套件熟悉和理解,更希望关注下微服务开发平台

概述

在使用springcloud ribbon客户端负载均衡的时候,可以给RestTemplate bean 加一个@LoadBalanced注解,就能让这个RestTemplate在请求时拥有客户端负载均衡的能力,先前有细嚼过但是没有做过笔记,刚好处理此类问题记录下

@LoadBalanced

  1. /**
  2. * 注释将RestTemplate bean标记为配置为使用LoadBalancerClient。
  3. */
  4. @Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
  5. @Retention(RetentionPolicy.RUNTIME)
  6. @Documented
  7. @Inherited
  8. @Qualifier
  9. public @interface LoadBalanced {
  10. }

通过源码可以发现这是一个LoadBalanced标记注解并且标记了@Qualifier(基于Spring Boot的自动配置机制),我们可以溯源到LoadBalancerAutoConfiguration

LoadBalancerAutoConfiguration

  1. /**
  2. * 功能区的自动配置(客户端负载平衡)
  3. */
  4. @Configuration
  5. @ConditionalOnClass(RestTemplate.class)
  6. @ConditionalOnBean(LoadBalancerClient.class)
  7. @EnableConfigurationProperties(LoadBalancerRetryProperties.class)
  8. public class LoadBalancerAutoConfiguration {
  9. @LoadBalanced
  10. @Autowired(required = false)
  11. private List<RestTemplate> restTemplates = Collections.emptyList(); //这里持有@LoadBalanced标记的RestTemplate实例
  12. @Autowired(required = false)
  13. private List<LoadBalancerRequestTransformer> transformers = Collections.emptyList();
  14. @Bean
  15. public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(
  16. final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {
  17. return () -> restTemplateCustomizers.ifAvailable(customizers -> {
  18. for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
  19. for (RestTemplateCustomizer customizer : customizers) {
  20. //为restTemplate添加定制
  21. customizer.customize(restTemplate);
  22. }
  23. }
  24. });
  25. }
  26. // ...
  27. /**
  28. * 以下针对classpath存在RetryTemplate.class的情况配置,先忽略
  29. */
  30. @Configuration
  31. @ConditionalOnClass(RetryTemplate.class)
  32. public static class RetryAutoConfiguration {
  33. @Bean
  34. @ConditionalOnMissingBean
  35. public LoadBalancedRetryFactory loadBalancedRetryFactory() {
  36. return new LoadBalancedRetryFactory() {
  37. };
  38. }
  39. }
  40. // ...
  41. }

@LoadBalanced@Autowried结合使用,意思就是这里注入的RestTempate Bean是所有加有@LoadBalanced注解标记的(持有@LoadBalanced标记的RestTemplate实例)

这段自动装配的代码的含义不难理解,就是利用了RestTempllate的拦截器,使用RestTemplateCustomizer对所有标注了@LoadBalanced的RestTemplate Bean添加了一个LoadBalancerInterceptor拦截器,而这个拦截器的作用就是对请求的URI进行转换获取到具体应该请求哪个服务实例ServiceInstance。

关键问下自己:为什么?

  • RestTemplate实例是怎么被收集的?
  • 怎样通过负载均衡规则获取具体的具体的server?

继续扒看源码>

上面可以看出,会LoadBalancerAutoConfiguration类对我们加上@LoadBalanced注解的bean 添加loadBalancerInterceptor拦截器

LoadBalancerInterceptor

  1. /**
  2. * 功能区的自动配置(客户端负载平衡)。
  3. */
  4. public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {
  5. private LoadBalancerClient loadBalancer;
  6. private LoadBalancerRequestFactory requestFactory;
  7. public LoadBalancerInterceptor(LoadBalancerClient loadBalancer,
  8. LoadBalancerRequestFactory requestFactory) {
  9. this.loadBalancer = loadBalancer;
  10. this.requestFactory = requestFactory;
  11. }
  12. public LoadBalancerInterceptor(LoadBalancerClient loadBalancer) {
  13. // for backwards compatibility
  14. this(loadBalancer, new LoadBalancerRequestFactory(loadBalancer));
  15. }
  16. @Override
  17. public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
  18. final ClientHttpRequestExecution execution) throws IOException {
  19. final URI originalUri = request.getURI();
  20. String serviceName = originalUri.getHost();
  21. Assert.state(serviceName != null,
  22. "Request URI does not contain a valid hostname: " + originalUri);
  23. return this.loadBalancer.execute(serviceName,
  24. this.requestFactory.createRequest(request, body, execution));
  25. }
  26. }

重点看intercept方法 当我们restTemplate执行请求操作时,就会被拦截器拦截进入intercept方法,而loadBalancer是LoadBalancerClient的具体实现

RibbonLoadBalancerClient

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

看到这里相信都遇到过类似的错误,恍然大悟

  1. No instances available for xxxxx

总结

  • 1.根据serviceId 获取对应的loadBalancer
  • 2.根据loadBalancer获取具体的server(这里根据负载均衡规则,获取到具体的服务实例)
  • 3.创建RibbonServer
  • 4.执行具体请求

这里

注意: @LoadBalanced 标记注解获取到最后通过负载均衡规则获取具体的具体的server来发起请求

案例

  1. /**
  2. * 服务注册中心配置
  3. *
  4. * @author <a href="mailto:shangzhi.ibyte@gmail.com">iByte</a>
  5. * @since 1.0.1
  6. */
  7. @Configuration
  8. @EnableConfigurationProperties(ModuleMappingHelper.class)
  9. public class DiscoveryConfig {
  10. @Autowired
  11. Environment environment;
  12. /**
  13. * DiscoveryHeaderHelper默认bean
  14. * @return
  15. */
  16. @Bean
  17. public DiscoveryHeaderHelper discoveryHeaderHelper() {
  18. DiscoveryHeaderHelper discoveryHeaderHelper = new DiscoveryHeaderHelper(environment);
  19. DiscoveryHeaderHelper.INSTANCE = discoveryHeaderHelper;
  20. return discoveryHeaderHelper;
  21. }
  22. /**
  23. * resttemplate构建
  24. */
  25. @Resource
  26. private RestTemplateBuilder restTemplateBuilder;
  27. /**
  28. * resttemplate请求bean,更改系统本身的builder
  29. * @return
  30. */
  31. @Bean
  32. @LoadBalanced
  33. public RestTemplate restTemplate() {
  34. RestTemplate restTemplate = restTemplateBuilder.configure(new RestTemplate());
  35. //RestTemplate interceptors 远程调用请求增加头部信息处理
  36. restTemplate.getInterceptors().add(new RestApiHeaderInterceptor());
  37. //RestTemplate Set the error handler 错误处理
  38. restTemplate.setErrorHandler(new RestResponseErrorHandler());
  39. return restTemplate;
  40. }
  41. @Bean
  42. public DiscoveryClient.DiscoveryClientOptionalArgs discoveryClientOptionalArgs() {
  43. DiscoveryClient.DiscoveryClientOptionalArgs discoveryClientOptionalArgs = new DiscoveryClient.DiscoveryClientOptionalArgs();
  44. discoveryClientOptionalArgs.setAdditionalFilters(Collections.singletonList(new DiscoveryHeaderClientFilter()));
  45. discoveryClientOptionalArgs.setEventListeners(Collections.singleton(new EurekaClientEventListener()));
  46. return discoveryClientOptionalArgs;
  47. }
  48. }

源码地址 > DiscoveryConfig

深入理解@LoadBalanced注解的实现原理与客户端负载均衡的更多相关文章

  1. 为何一个@LoadBalanced注解就能让RestTemplate拥有负载均衡的能力?【享学Spring Cloud】

    每篇一句 你应该思考:为什么往往完成比完美更重要? 前言 在Spring Cloud微服务应用体系中,远程调用都应负载均衡.我们在使用RestTemplate作为远程调用客户端的时候,开启负载均衡极其 ...

  2. Spring Cloud(十四):Ribbon实现客户端负载均衡及其实现原理介绍

    年后到现在一直很忙,都没什么时间记录东西了,其实之前工作中积累了很多知识点,一直都堆在备忘录里,只是因为近几个月经历了一些事情,没有太多的经历来写了,但是一些重要的东西,我还是希望能坚持记录下来.正好 ...

  3. Ribbon使用及其客户端负载均衡实现原理分析

    1.ribbon负载均衡测试 (1)consumer工程添加依赖 <dependency> <groupId>org.springframework.cloud</gro ...

  4. Nginx 负载均衡原理简介与负载均衡配置详解

    Nginx负载均衡原理简介与负载均衡配置详解   by:授客  QQ:1033553122   测试环境 nginx-1.10.0 负载均衡原理 客户端向反向代理发送请求,接着反向代理根据某种负载机制 ...

  5. HA主备路由模式的原理 + HA和负载均衡的区别

       HA主备路由模式的原理 HA是High Availability缩写,即高可用性 ,可防止网络中由于单个防火墙的设备故障或网络故障导致网络中断,保证网络服务的连续性和安全强度.目前,ha功能已经 ...

  6. springcloud ribbon的 @LoadBalanced注解

    在使用springcloud ribbon客户端负载均衡的时候,可以给RestTemplate bean 加一个@LoadBalanced注解,就能让这个RestTemplate在请求时拥有客户端负载 ...

  7. Spring cloud 之Ribbon(二)负载均衡原理

    ribbon实现负载均衡的原理 我们从Ribbon实现负载均衡的代码可以看到,Ribbon是通过RestTemPlate实现客户端负载均衡的,准确的说是RestTemPlate上的@LoadBalan ...

  8. SpringCloud学习笔记(7)----Spring Cloud Netflix之负载均衡-Ribbon的深入理解

    1. 注解@LoadBalanced 作用:识别应用名称,并进行负载均衡. 2. 入口类:LoadBalancerAutoConfiguration 说明:类头上的注解可以知道Ribbon 实现的负载 ...

  9. F5负载均衡原理

    一. 负载均衡技术 负载均衡技术在现有网络结构之上提供了一种廉价.有效.透明的方法,来扩展网络设备和服务器的带宽.增加吞吐量.加强网络数据处理能力.提高网络的灵活性和可用性. 1.负载均衡发生的流程图 ...

随机推荐

  1. atomic_inc(&v)原子操作简述

    atomic_inc(&v)对变量v用锁定总线的单指令进行不可分解的"原子"级增量操作,避免v的值由于中断或多处理器同时操作造成不确定状态. 原子操作 所谓原子操作,就是该 ...

  2. python编程基础之十九

    字符串判断常用函数: str.isalpha()  # 判断是否是全字母 str.isdigit()   # 判断是否是全数字 str.isalnum()  # 判断是否是字母和数字组合 str.is ...

  3. 洛谷P1613 跑路

    题目描述 小A的工作不仅繁琐,更有苛刻的规定,要求小A每天早上在6:00之前到达公司,否则这个月工资清零.可是小A偏偏又有赖床的坏毛病.于是为了保住自己的工资,小A买了一个十分牛B的空间跑路器,每秒钟 ...

  4. Linux之shell基础

    Shell基础 一.shell概述 1) shell是一个命令行解释器,它为用户提供了一个向Linux内核发送请求以便运行程序的界面系统级程序,用户可以用shell来启动.挂起.停止甚至是编写一些程序 ...

  5. 【Medium 万赞好文】ViewModel 和 LIveData:模式 + 反模式

    原文作者: Jose Alcérreca 原文地址: ViewModels and LiveData: Patterns + AntiPatterns 译者:秉心说 View 和 ViewModel ...

  6. php函数fsockopen的使用

    函数说明:fsockopen — 打开一个网络连接或者一个Unix套接字连接 语法: resource fsockopen ( string $hostname [, int $port = -1 [ ...

  7. [NOIp2011] luogu P1311 选择客栈

    我妈的抽象歌曲真 nb. 题目描述 给你 nnn 个点,每个点有两个参数 ci,dic_i,d_ici​,di​,给你一个数 DDD.定义一种方案合法,当且仅当你选出整数 i,j∈[1,n],i< ...

  8. [JZOJ5781]【NOIP提高A组模拟2018.8.8】秘密通道

    Description 有一副n*m的地图,有n*m块地,每块是下列四种中的一种:墙:用#表示,墙有4个面,分别是前面,后面,左面,右面.起点:用C表示,为主角的起点,是一片空地.终点:用F表示,为主 ...

  9. windows下安装scoop

    scoop是windows下的包管理工具,类似与linux下的yum和python的pip. scoop可以在windows下方便的进行软件的管理,尤其是对开发者提供了很大的遍历. cmd下执行如下红 ...

  10. spring security原理-学习笔记2-核心组件

    核心组件 AuthenticationManager,ProviderManager和AuthenticationProvider AuthenticationManager只是一个接口,实际中是如何 ...