SpringCloud超详细讲解负载均衡组件Ribbon源码

目录
  • 前言
  • 项目实战
    • 创建项目
    • 启动项目验证
  • 源码分析
    • 选择服务
    • 地址替换
  • 总结

前言

上一篇文章中我们通过自己开发了一个负载均衡组件,实现了随机算法的负载均衡功能,如果要实现其他算法,还需要修改代码增加相应的功能。这一篇文章,我们将介绍一个更简单的负载均衡实现,使用**@LoadBalanced**注解实现负载均衡的功能。

项目实战

创建项目

同样的,我们的项目现在依然有一个registry注册中心,一个provider服务提供者,接下来,我们再次修改一下consumer服务消费者的代码:

@EnableEurekaClient
@SpringBootApplication
@RestController
public class ConsumerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class, args);
    }
    @Bean
    @LoadBalanced
    RestTemplate restTemplate() {
        return new RestTemplate();
    }
    @Autowired
    DiscoveryClient discoveryClient;
    @Autowired
    RestTemplate restTemplate;
    @GetMapping("/hello2")
    public String hello2(String name) {
        String returnInfo = restTemplate.getForObject(  "http://provider/hello?name={1}", String.class, name);
        return returnInfo;
    }
}

在这个版本,同样的,还是创建RestTemplate Bean对象,不同的是上面仅仅增加了@LoadBalanced注解。

启动项目验证

依然正确返回了结果!

太神奇了吧,比我们自己开发的负载均衡组件简单太多了吧,仅仅在restTemplate() 方法上面增加了一个@LoadBalanced注解,怎么就实现的呢?废话不说,为了一探究竟,扒一扒源码吧!

源码分析

首先,点击@LoadBalanced注解进去,没有什么特别之处,那么我们在想想,Spring在创建Bean实例的时候,注解在什么地方起了作用?什么?不知道?翻一下这篇文章吧:

肝了两周,一张图解锁Spring核心源码

通过回顾Spring启动以及Bean的生命周期创建过程,我们就会发现加上@LoadBalancer注解后,项目启动时就会加载LoadBalancerAutoConfiguration这个配置类(通过spring-cloud-commons包下面的的spring.factories)。通过查看该配置类源码,发现其有个静态内部类LoadBalancerInterceptorConfig,其内部又创建了一个负载均衡拦截器:LoadBalancerInterceptor,该拦截器包含有一个loadBalancerClient参数:

@ConditionalOnMissingClass({"org.springframework.retry.support.RetryTemplate"})
    static class LoadBalancerInterceptorConfig {
        LoadBalancerInterceptorConfig() {
        }
        @Bean
        public LoadBalancerInterceptor ribbonInterceptor(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);
            };
        }
    }

我们继续点击LoadBalancerInterceptor类进入,发现intercept方法,该方法中调用了LoadBalancerClient的execute方法,

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

LoadBalancerClient是一个接口,点击进去我们发现其实现类是RibbonLoadBalancerClient,查看其继承关系:

通过接口中的方法名称,我们可以猜想,choose方法就是选择其中服务列表中其中一个服务,reconstructURI方法就是重新构造请求的URI。

选择服务

choose方法是在RibbonLoadBalancerClient实现类中实现的

    public ServiceInstance choose(String serviceId, Object hint) {
        Server server = this.getServer(this.getLoadBalancer(serviceId), hint);
        return server == null ? null : new RibbonLoadBalancerClient.RibbonServer(serviceId, server, this.isSecure(server, serviceId), this.serverIntrospector(serviceId).getMetadata(server));
    }

在这个方法中,先调用 getServer 方法获取服务,这个方法最终会调用 ILoadBalancer 接口的 chooseServer 方法,而 ILoadBalancer 接口的实现类默认是ZoneAwareLoadBalancer。

ZoneAwareLoadBalancer 继承自 DynamicServerListLoadBalancer ,而在 DynamicServerListLoadBalancer 的构造方法中,调用了 this.restOfInit(clientConfig);在restOfInit这个方法中,通过 this.updateListOfServers()来获取服务列表;

而在chooseServer ()方法中,就会根据负载均衡算法,选择其中一个服务并返回:

 BaseLoadBalancer zoneLoadBalancer = this.getLoadBalancer(zone);
 server = zoneLoadBalancer.chooseServer(key);

地址替换

选择其中一个服务信息后,怎么将接口从 http://provider/hello 变为 http://localhost:8003/hello 呢?还记得上面我们说的reconstructURI方法吗?通过配置类LoadBalancerAutoConfiguration加载后,会注入LoadBalancerInterceptor拦截器,该拦截器会拦截我们的请求,并对请求地址进行处理,重构方法的具体实现在 LoadBalancerContext 类的 reconstructURIWithServer 方法中

public URI reconstructURIWithServer(Server server, URI original) {
        String host = server.getHost();
        int port = server.getPort();
        String scheme = server.getScheme();
        if (host.equals(original.getHost()) && port == original.getPort() && scheme == original.getScheme()) {
            return original;
        } else {
            if (scheme == null) {
                scheme = original.getScheme();
            }
            if (scheme == null) {
                scheme = (String)this.deriveSchemeAndPortFromPartialUri(original).first();
            }
            try {
                StringBuilder sb = new StringBuilder();
                sb.append(scheme).append("://");
                if (!Strings.isNullOrEmpty(original.getRawUserInfo())) {
                    sb.append(original.getRawUserInfo()).append("@");
                }
                sb.append(host);
                if (port >= 0) {
                    sb.append(":").append(port);
                }
                sb.append(original.getRawPath());
                if (!Strings.isNullOrEmpty(original.getRawQuery())) {
                    sb.append("?").append(original.getRawQuery());
                }
                if (!Strings.isNullOrEmpty(original.getRawFragment())) {
                    sb.append("#").append(original.getRawFragment());
                }
                URI newURI = new URI(sb.toString());
                return newURI;
            } catch (URISyntaxException var8) {
                throw new RuntimeException(var8);
            }
        }
    }

可以看到该方法中,将原始的请求地址original,替换成了选取的服务的IP和端口。并最终调用该服务的接口方法。

看到这里,再想想我们上一章的内容,是不是有异曲同工之妙?

总结

通过添加@LoadBalanced注解,就及其简单的实现了负载均衡的功能,与其说是Ribbon的强大,不如说是Spring的强大,Spring在整个上下文创建过程中,在不同的时机开放了一个又一个的接口,这就为各种组件的继承提供了遍历,同时也进一步促进了Spring生态的快速发展。

到此这篇关于SpringCloud超详细讲解负载均衡组件的文章就介绍到这了,更多相关SpringCloud负载均衡组件内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • SpringCloud如何实现Zuul集群(负载均衡)

    目录 前言: 一.使用 Nginx+Zuul 实现网关集群 1.创建Eurek注册中心.会员服务.订单服务 (略) 2. 创建Zuul服务 3. 下载Nginx服务器 二. 测试 三.补充 Nginx和网关的区别在什么地方? Nginx也可以实现网关,为什么不用Nginx实现网关呢? 关于Nginx负载均衡故障转移: 前言: 在微服务架构中,有一个组件可以说是必不可少的,那就是微服务网关,微服务网关处理了负载均衡,缓存,路由,访问控制,服务代理,监控,日志等.API网关在微服务架构中正是以微服务

  • 详解SpringCloud的负载均衡

    目录 一.什么是负载均衡 二.负载均衡的简单分类 三.为什么需要做负载均衡 四.springCloud如何开启负载均衡 五.IRule 1.RandomRule:表示随机策略,它将从服务清单中随机选择一个服务: 2.ClientConfigEnabledRoundRobinRule:ClientConfigEnabledRoundRobinRule并没有实现什么特殊的处理逻辑,但是他的子类可以实现一些高级策略, 当一些本身的策略无法实现某些需求的时候,它也可以做为父类帮助实现某些策略,一般情况下

  • SpringCloud 客户端Ribbon负载均衡的实现方法

    目录 Ribbon 介绍 开启客户端负载均衡,简化 RestTemplate 调用 负载均衡策略 饥饿加载 Ribbon 介绍 Ribbon 是 Netflix 提供的一个基于 Http 和 TCP 的客户端负载均衡工具,且已集成在 Eureka 依赖中. 实现原理:SpringCloud Ribbon 的底层采用了一个拦截器,拦截了 RestTemplate 发出的请求,对地址做了修改. 开启客户端负载均衡,简化 RestTemplate 调用 1)在服务调用者的 RestTemplate 配

  • springcloud gateway如何实现路由和负载均衡

    简介: gateway主要是做路由 负载,过滤 主要是替代zuul 1.x 性能比zuul好 zuul是基于 Servlet ,gateway是基于spring-webflux 用的netty+reactor yml文件 实现路由 负载 的配置 亲自测试 spring: application: name: xgyx_gateway cloud: discovery: locator: enabled: true gateway: routes: - id: a #随便定义不重复就好 uri:

  • SpringCloud Zuul实现负载均衡和熔断机制方式

    一.场景 笔者就Zuul网关下实现其负载均衡与熔断机制(雪崩)进行实践,前提是已经导入zuul相关依赖 springboot版本:1.5.9.RELEASE springcloud版本:Dalston.SR5 <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zuul</artif

  • SpringCloud笔记(Hoxton)Netflix之Ribbon负载均衡示例代码

    目录 Ribbon使用 负载均衡 代码示例 注册中心 Provider 接口实现 Consumer 添加依赖 测试 Ribbon使用 Ribbon是管理HTTP和TCP服务客户端的负载均衡器,Ribbon具有一系列带有名称的客户端(Named Client),也就是带有名称的Ribbon客户端(Ribbon Client). 每个客户端由可配置的组件构成,负责一类服务的调用请求.SpringCloud通RibbonClientConfiguration 为每个Ribbon客户端创建Applica

  • 深入理解Java SpringCloud Ribbon 负载均衡

    目录 前言 1.抛出问题 2.源码解析 2.1.LoadBalancerIntercepor 2.2.LoadBalancerClient 2.3.负载均衡策略IRule 2.4.总结 3.负载均衡策略 总结 前言 该技术博客是关于黑马视频教程的笔记总结! 服务消费者需要通过RestTemplate调用注册中心(Eureka)的服务提供者,但当同一服务名称的服务有多个的时候,我们的服务消费者应该调用哪一个服务呢?这时候就需要我们学习理解Ribbon负载均衡的实现原理. 当我们在RestTempl

  • SpringCloud Gateway详细分析实现负载均衡与熔断和限流

    目录 环境准备 1.pom依赖 2.yaml配置 3.路由转发和负载均衡测试 user服务暴露接口 返回结果输出 4.gateway熔断实现 4.1 熔断代码 4.2 测试 5.gateway限流 5.1 需要集成redis 5.2 yaml配置 5.3 注入到spring容器 5.4 测试 环境准备 注册中心Nacos,也可以其他 springboot 2.6.8 spring-cloud-dependencies 2021.0.3 1.pom依赖 parent包 <parent> <

  • SpringCloud LoadBalancerClient 负载均衡原理解析

    目录 深入解析 LoadBalancerClient 接口源码: 1.LoadBalancerClient 源码解析: 2.ILoadBalancer 源码解析: LoadBalancerClient 是 SpringCloud 提供的一种负载均衡客户端,Ribbon 负载均衡组件内部也是集成了 LoadBalancerClient 来实现负载均衡.那么 LoadBalancerClient 内部到底是如何做到的呢?我们先大概讲一下 LoadBalancerClient 的实现原理,然后再深入源

  • SpringCloud超详细讲解负载均衡组件Ribbon源码

    目录 前言 项目实战 创建项目 启动项目验证 源码分析 选择服务 地址替换 总结 前言 上一篇文章中我们通过自己开发了一个负载均衡组件,实现了随机算法的负载均衡功能,如果要实现其他算法,还需要修改代码增加相应的功能.这一篇文章,我们将介绍一个更简单的负载均衡实现,使用**@LoadBalanced**注解实现负载均衡的功能. 项目实战 创建项目 同样的,我们的项目现在依然有一个registry注册中心,一个provider服务提供者,接下来,我们再次修改一下consumer服务消费者的代码: @

  • SpringCloud超详细讲解Feign声明式服务调用

    目录 入门案例 @FeignClient注解详解 Feign Client的配置 Feign请求添加headers 负载均衡 (Ribbon) 容错机制 Hystrix支持 Sentinel支持 Feign开启容错机制支持后的使用方式 请求压缩feign.compression 日志级别 入门案例 在服务消费者导入依赖 <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>

  • SpringCloud超详细讲解微服务网关Zuul

    目录 网关的作用 Spring Cloud 网关组件Zuul介绍 Zuul网关实战 1.创建服务 2.创建配置文件 3.创建Zuul过滤器 4.编写启动类 5.启动验证 总结 网关的作用 微服务架构中,服务实例的地址可能经常会发生变化,所以我们不能直接将服务的地址暴露出来.如果每一个微服务都直接暴露接口,会导致一系列的问题,比如调用过于复杂,涉及到账户.权限不能统一处理等.另外基于高内聚低耦合的设计准则来讲,我们也应该将内部系统和外部系统做切割. 因此,这时就需要有一个独立的组件来处理外部的请求

  • Spring Cloud负载均衡组件Ribbon原理解析

    目录 前言 一个问题引发的思考 Ribbon的简单使用 Ribbon 原理分析 LoadBalancerAutoConfiguration 自动装配 RestTemplateCustomizer LoadBalancerInterceptor RibbonLoadBalancerClient#execute ZoneAwareLoadBalancer 负载均衡器 如何获取所有服务 如何判断服务是否可用 Ribbon 的负载均衡算法 总结 微服务体系下的 Spring Cloud Netflix

  • SpringCloud超详细讲解微服务网关Gateway

    目录 前言 微服务网关GateWay介绍 GateWay特性介绍 Gateway 中的相关术语 Gateway实战 1.创建项目gateway 2.创建启动类 3.新增配置文件 4.编程方式实现路由 5.启动验证 总结 前言 上一篇:微服务网关Zuul 上文中,我们介绍了微服务网关Zuul,Zuul 是 Netflix 公司开源的产品,被称为第一代网关,也是 Spring Cloud 前几个版本默认使用的一款提供动态路由微服务网关组件,但是随着 Netflix 公司一系列的停更事件,在最新的 S

  • 超详细讲解SpringCloud Commons公共抽象的用法

    目录 Spring Cloud Commons公共抽象 @EnableDiscoveryClient 服务注册ServiceRegistry RestTemplate的负载均衡 RestTemplate的失败重试 本期主角——Spring Cloud Commons:公共抽象 Spring Cloud Commons公共抽象 Spring Cloud将服务发现.负载均衡和断路器等通用模型封装在一个公共抽象中,可以被所有的Spring Cloud客户端使用,不依赖于具体的实现(例如服务发现就有Eu

  • SpringCloud Feign超详细讲解

    目录 一.什么是Feign 二.Feign能干什么 三.Feign的使用步骤 1.新建一个module 2.配置Pom.xml 3.配置applicatin.yaml 4.配置configBean 5.配置Controller类 6.配置启动类 7.改动API 1)引入Feign依赖 2)配置Service 3)注意 四.结果 一.什么是Feign Feign是声明式Web Service客户端,它让微服务之间的调用变得更简单,类似controller调用service.SpringCloud集

  • Android超详细讲解组件AdapterView的使用

    目录 概述 介绍AdapterView的编程模式 Adapter ListView使用 myAdapater.java MainActivity.java activity_main.xml myAdapater.java MainActivity.java 概述 在Android应用开发中,AdapterView是一类常用且非常重要的组件.我们常见的以列表的形式显示信息的组件就是AdapterView的子类,称为Listview:我们经常以网格方式浏览图片缩略图的组件也是AdapterView

  • Android超详细讲解组件LinearLayout的使用

    目录 概述 常用XML配置属性 (1) android:orientation (2) android:gravity (3) View中继承来的属性 代码举例 概述 LinearLayout是线性布局组件,放置在其中的组件按列或者按行(就是垂直或者水平)的方式排序分布. 常用XML配置属性 (1) android:orientation 设置LinearLayout容器布局组件的方式:只能取值:horizontal(水平的),vertical(垂直的) (2) android:gravity

  • Android超详细讲解组件ScrollView的使用

    目录 概述 练习 HorizontalScrollView: 概述 ScrollView也是一个容器,它是FrameLayout的子类,它的主要作用就是将超出物理屏幕的内容显示出来,(就是滚动条效果)ScrollView提供垂直滚动,进而可将超出物理屏幕的内容显示出来. 在一般情况下,可以将一个采用垂直方式布局组件的LinearLayout作为ScrollLayout容器的子组件,同时,在LinearLayout容器中可以显示超出屏幕物理高度的内容. 练习 这么说有点抽象,然后我们现在实现完成一

随机推荐