Ribbon LoadBalanced底层机制源码探秘

这篇具有很好参考价值的文章主要介绍了Ribbon LoadBalanced底层机制源码探秘。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

🍊 Java学习:社区快速通道

🍊 深入浅出RocketMQ设计思想:深入浅出RocketMQ设计思想

🍊 绝对不一样的职场干货:大厂最佳实践经验指南


📆 最近更新:2023年6月18日


🍊 点赞 👍 收藏 ⭐留言 📝 都是我最大的动力!


通过本文你可以学习到:

  1. LoadBalanced作用原理
  2. 拦截器到Rule的调角链路
  3. IPing机制

负载均衡器LoadBalancer原理

一句话概括: LoadBalancedRestTemplate上打标,Ribbon将带有负载均衡能力的拦截器注入标记好的RestTemplate中,以此实现负载均衡。


@LoadBalanced开始看起:

它会将RestTemplate传送到Ribbon的自动装配类里进行改造。

Ribbon LoadBalanced底层机制源码探秘

  • @LoadBalanced:这个注解即修饰RestTemplate,还修饰LoadBalancerAutoConfiguration。它会将所有带有LoadBalanced注解的RestTemplate类,都传入到LoadBalancerAutoConfiguration中。这个注解的定义上还有一个@Qualifier注解,@Qualifier注解搭配@Autowired注解做自动装配,可以通过name属性,将指定的Bean装载到指定位置。

这里LoadBalanced也是借助Qualifier实现了一个给RestTemplate打标的功能,凡是被打标的RestTemplate都会被传送到AutoConfig中做进一步改造。

  • LBAutoConfig:从上一步中传送过来的RestTemplate,会经过LBAutoConfig的装配,将一系列的拦截器添加到RestTemplate中。Ribbon拦截器会拦截每个网络请求做一番处理,在这个过程中拦截器会找到对应的LoadBalancer对HTTP请求进行接管,接着LoadBalancer就会找到默认或指定的负载均衡策略来对HTTP请求进行转发。

拦截器是类似职责链设计模型的结构,常见的ServletFilter,权限控制器等都是类似的模式。


Ribbon LoadBalanced底层机制源码探秘

点进LoadBalanced注解,查到LoadBalancerAutoConfiguration使用了该注解

Ribbon LoadBalanced底层机制源码探秘
该注解可以把修饰的restTemplate传送到LoadBalancerAutoConfiguration


这个restTemplate只在loadBalancedRestTemplateInitializerDeprecated方法里被用到

@Bean
public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {
    return () -> {
        restTemplateCustomizers.ifAvailable((customizers) -> {
            Iterator var2 = this.restTemplates.iterator();

            while(var2.hasNext()) {
                RestTemplate restTemplate = (RestTemplate)var2.next();
                Iterator var4 = customizers.iterator();

                while(var4.hasNext()) {
                    RestTemplateCustomizer customizer = (RestTemplateCustomizer)var4.next();
                    customizer.customize(restTemplate);
                }
            }

        });
    };
}

循环访问所有的restTemplaterestTemplateCustomizers是由外面初始化的bean注入进来的,使用customizerrestTemplate做手脚


@Bean
@ConditionalOnMissingBean
public RestTemplateCustomizer restTemplateCustomizer(final LoadBalancerInterceptor loadBalancerInterceptor) {
    return (restTemplate) -> {
        List<ClientHttpRequestInterceptor> list = new ArrayList(restTemplate.getInterceptors());
        list.add(loadBalancerInterceptor);
        restTemplate.setInterceptors(list);
    };
}

先从restTemplate获取getInterceptors(),接下来list里添加一个loadBalancerInterceptor,它的注入:

@Bean
public LoadBalancerInterceptor ribbonInterceptor(LoadBalancerClient loadBalancerClient, LoadBalancerRequestFactory requestFactory) {
    return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
}
public void setInterceptors(List<ClientHttpRequestInterceptor> interceptors) {
    if (this.interceptors != interceptors) {
        this.interceptors.clear();
        this.interceptors.addAll(interceptors);
        AnnotationAwareOrderComparator.sort(this.interceptors);
    }
}

这里把 interceptors 和本地保存的做一下比较,如果不一样则本地的interceptors全部清空,然后添加上新的,再sort一下


真正起作用的位置是在LoadBalancerInterceptorintercept方法上

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));
}

先从url中得到uri,再从uri里得到serviceName(要去访问的serviceName),然后执行execute方法

public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint) throws IOException {
    ILoadBalancer loadBalancer = this.getLoadBalancer(serviceId);
    Server server = this.getServer(loadBalancer, hint);
    if (server == null) {
        throw new IllegalStateException("No instances available for " + serviceId);
    } else {
        RibbonLoadBalancerClient.RibbonServer ribbonServer = new RibbonLoadBalancerClient.RibbonServer(serviceId, server, this.isSecure(server, serviceId), this.serverIntrospector(serviceId).getMetadata(server));
        return this.execute(serviceId, (ServiceInstance)ribbonServer, (LoadBalancerRequest)request);
    }
}

这里到了真正执行任务的时候了,先根据serviceId获取一个LoadBalancer,拿到负载均衡策略之后用getServer获取到真正的server

protected Server getServer(ILoadBalancer loadBalancer, Object hint) {
    return loadBalancer == null ? null : loadBalancer.chooseServer(hint != null ? hint : "default");
}
@Override
public Server chooseServer(Object key) {
    if (!ENABLED.get() || getLoadBalancerStats().getAvailableZones().size() <= 1) {
        logger.debug("Zone aware logic disabled or there is only one zone");
        return super.chooseServer(key);
    }
    Server server = null;
    try {
        LoadBalancerStats lbStats = getLoadBalancerStats();
        Map<String, ZoneSnapshot> zoneSnapshot = ZoneAvoidanceRule.createSnapshot(lbStats);
        logger.debug("Zone snapshots: {}", zoneSnapshot);
        if (triggeringLoad == null) {
            triggeringLoad = DynamicPropertyFactory.getInstance().getDoubleProperty(
                    "ZoneAwareNIWSDiscoveryLoadBalancer." + this.getName() + ".triggeringLoadPerServerThreshold", 0.2d);
        }

        if (triggeringBlackoutPercentage == null) {
            triggeringBlackoutPercentage = DynamicPropertyFactory.getInstance().getDoubleProperty(
                    "ZoneAwareNIWSDiscoveryLoadBalancer." + this.getName() + ".avoidZoneWithBlackoutPercetage", 0.99999d);
        }
        Set<String> availableZones = ZoneAvoidanceRule.getAvailableZones(zoneSnapshot, triggeringLoad.get(), triggeringBlackoutPercentage.get());
        logger.debug("Available zones: {}", availableZones);
        if (availableZones != null &&  availableZones.size() < zoneSnapshot.keySet().size()) {
            String zone = ZoneAvoidanceRule.randomChooseZone(zoneSnapshot, availableZones);
            logger.debug("Zone chosen: {}", zone);
            if (zone != null) {
                BaseLoadBalancer zoneLoadBalancer = getLoadBalancer(zone);
                server = zoneLoadBalancer.chooseServer(key);
            }
        }
    } catch (Exception e) {
        logger.error("Error choosing server using zone aware logic for load balancer={}", name, e);
    }
    if (server != null) {
        return server;
    } else {
        logger.debug("Zone avoidance logic is not invoked.");
        return super.chooseServer(key);
    }
}

如果只定义了一个defaultZone,则会调用父类的chooseServer方法

public Server chooseServer(Object key) {
    if (counter == null) {
        counter = createCounter();
    }
    counter.increment();
    if (rule == null) {
        return null;
    } else {
        try {
            return rule.choose(key);
        } catch (Exception e) {
            logger.warn("LoadBalancer [{}]:  Error choosing server for key {}", name, key, e);
            return null;
        }
    }
}

这里使用默认的负载均衡策略RandomRule


回到RibbonLoadBalancerClientexecute方法,获取到的服务器不为空则:

RibbonLoadBalancerClient.RibbonServer ribbonServer = new RibbonLoadBalancerClient.RibbonServer(serviceId, server, this.isSecure(server, serviceId), this.serverIntrospector(serviceId).getMetadata(server));
return this.execute(serviceId, (ServiceInstance)ribbonServer, (LoadBalancerRequest)request);

构建一个RibbonServer,最后execute就是真正发起请求了文章来源地址https://www.toymoban.com/news/detail-488572.html


到了这里,关于Ribbon LoadBalanced底层机制源码探秘的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Kafka底层原理探秘

    Kafka 是一个分布式流处理平台由 LinkedIn 公司开发的,遵循 Apache 开源协议。 Kafka 主要是用来处理实时数据流,可以发布、订阅、存储和处理数据。 应用场景: 日志收集:用于分布式日志系统,例如 ELK。 消息系统:可以将 Kafka 作为消息队列使用。 流处理:将 Kafka 与 Flink 或

    2024年02月16日
    浏览(27)
  • Linux源码解读系列是一套深入剖析Linux内核源码的教程,旨在帮助读者理解Linux操作系统的底层原理和工作机制

    Linux源码解读系列是一套深入剖析Linux内核源码的教程,旨在帮助读者理解Linux操作系统的底层原理和工作机制。该系列教程从Linux内核的各个模块入手,逐一分析其源码实现,并结合实际应用场景进行讲解。通过学习本系列,读者可以深入了解Linux操作系统的底层机制,掌握

    2024年01月21日
    浏览(39)
  • 探秘MySQL底层架构:设计与实现流 程一览

    点赞还是要求一下的,万一屏幕前的大漂亮,还有大帅哥就点赞了呢!!!! Author: 源码时代 Raymon老师 Mysql,作为一款优秀而广泛使用的数据库管理系统,对于众多Java工程师来说,几乎是日常开发中必不可少的一环。无论是存储海量数据,还是高效地检索和管理数据,Mysq

    2024年02月15日
    浏览(28)
  • 探秘JavaScript事件传播机制:冒泡、捕获与目标阶段解析

    ​🌈个人主页:前端青山 🔥系列专栏:JavaScript篇 🔖 人终将被年少不可得之物困其一生 依旧 青山 ,本期给大家带来JavaScript篇专栏内容:JavaScript-事件传播 目录 事件 传播 阻止事件传播 默认行为 阻止浏览器默认行为 事件委托 target 封装事件库 浏览器内的事件流机制 什么是

    2024年02月05日
    浏览(33)
  • 【c/c++】深入探秘:C++内存管理的机制

    🔥个人主页 : Quitecoder 🔥 专栏 : c++笔记仓 朋友们大家好,本篇文章我们详细讲解c++中的动态内存管理 我们来看内存区域划分 数据段就是我们所说的 全局变量 ,代码段是我们所说的 常量区 ,我们需要重点关注的是 堆区 ,这部分是由我们自己控制的 我们来依次讨论:

    2024年04月16日
    浏览(24)
  • 通过一次线上问题,讲下Ribbon重试机制

    前段时间,产品经理在线上验证产品功能的时候,发现某个功能不符合需求预期,后来测试验证发现是服务端的一个接口大概率偶现超时,前端做了兜底处理,所以对线上用户么有太大影响。 由于服务端的接口偶现超时,并且网关设置了30s超时熔断,所以前端请求就直接报错

    2024年02月15日
    浏览(47)
  • “深入解析JVM内部机制:探秘Java虚拟机的奥秘“

    标题:深入解析JVM内部机制:探秘Java虚拟机的奥秘 摘要:本文将深入解析JVM(Java虚拟机)的内部机制,从字节码执行到垃圾回收,逐步揭示Java程序运行的奥秘。通过理论分析和示例代码,读者将对JVM的工作原理有更深入的了解。 正文: 一、Java虚拟机简介 Java虚拟机(JVM)

    2024年02月12日
    浏览(28)
  • 【网络安全】网络防护之旅 - Java安全机制探秘与数字证书引爆网络防线

    🌈个人主页: Sarapines Programmer 🔥 系列专栏: 《网络安全之道 | 数字征程》 ⏰墨香寄清辞:千里传信如电光,密码奥妙似仙方。 挑战黑暗剑拔弩张,网络战场誓守长。 目录 😈1. 初识网络安全 😈2. Java安全机制和数字证书的管理 🕵️‍♂️2.1 研究目的 🕵️‍♂️2.2 研

    2024年02月04日
    浏览(42)
  • 实现SpringMVC底层机制(二)

    1.修改SunWebApplicationContext.java 2.修改SunDispatcherServlet.java 1.需求分析 2.编写Monster.java 3.自定义Service注解 4.编写Service接口MonsterService.java 5.编写Service实现类MonsterServiceImpl.java 6.修改SunWebApplicationContext.java的executeInstance方法,增加对Service注解的扫描 7.debug测试 1.自定义Autowired注解 2

    2024年04月27日
    浏览(25)
  • 手写Spring底层机制

    加入到createBean()中 加入到createBean()中 AOP需要在后置处理器的before方法中实现 1.单例/多例怎么实现的?@scope为什么可以实现? 回答:@scope 的value属性可以设置为singleton /prototype 通过getBean()方法 如果bean中的属性scope为singleton 就从单例池直接拿,如果是prototype 就调用createB

    2024年04月09日
    浏览(31)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包