当前位置: 首页 > news >正文

Ribbon实现原理

文章目录

  • 概要
    • 什么是Ribbon
    • 客户端负载均衡
  • RestTemplate核心方法
    • GET 请求
      • getForEntity
      • getForObject
    • POST 请求
      • postForEntity
      • postForObject
      • postForLocation
    • PUT请求
    • DELETE请求
  • 源码分析
    • 类图关系
  • 与Eureka结合
  • 重试机制

概要

什么是Ribbon

Spring Cloud Ribbon是一个基于HTTP和TCP的客户端负载均衡工具,它基于Netflix Ribbon实现。通过Spring Cloud的封装,可以让我们轻松地将面向服务的REST模板请求自动转换成客户端负载均衡的服务调用。Spring Cloud Ribbon虽然只是一个工具类框架,它不像服务注册中心、配置中心、API 网关那样需要独立部署,但是它几乎存在于每一个Spring Cloud构建的微服务和基础设施中。因为微服务间的调用,API网关的请求转发等内容,实际上都是通过Ribbon来实现的,包括后续我们将要介绍的Feign,它也是基于Ribbon实现的工具。

客户端负载均衡

负载均衡在系统架构中是一个非常重要,并且是不得不去实施的内容。因为负载均衡是对系统的高可用、网络压力的缓解和处理能力扩容的重要手段之一。我们通常所说的负载均衡都指的是服务端负载均衡,其中分为硬件负载均衡和软件负载均衡。硬件负载均衡主要通过在服务器节点之间安装专门用于负载均衡的设备,比如F5等;而软件负载均衡则是通过在服务器上安装一些具有均衡负载功能或模块的软件来完成请求分发工作,比如Nginx 等。

硬件负载均衡的设备或是软件负载均衡的软件模块都会维护一个下挂可用的服务端清单,通过心跳检测来剔除故障的服务端节点以保证清单中都是可以正常访问的服务端节点。当客户端发送请求到负载均衡设备的时候,该设备按某种算法(比如线性轮询、按权重负载、按流量负载等)从维护的可用服务端清单中取出一台服务端的地址,然后进行转发。

而客户端负载均衡和服务端负载均衡最大的不同点在于上面所提到的服务清单所存储的位置。在客户端负载均衡中,所有客户端节点都维护着自己要访问的服务端清单,而这些服务端的清单来自于服务注册中心,比如上一章我们介绍的Eureka服务端。同服务端负载均衡的架构类似,在客户端负载均衡中也需要心跳去维护服务端清单的健康性,只是这个步骤需要与服务注册中心配合完成。在Spring Cloud实现的服务治理框架中,默认会创建针对各个服务治理框架的 Ribbon 自动化 整 合 配 置 , 比 如 Eureka 中 的org.springframework.cloud.netflix.ribbon.eureka.RibbonEurekaAutoConfiguration,Consul 中 的org.springframework.cloud.consul.discovery.RibbonConsulAutoConfiguration。在实际使用的时候,我们可以通过查看这两个类的实现,以找到它们的配置详情来帮助我们更好地使用它。

通过Spring Cloud Ribbon的封装,我们在微服务架构中使用客户端负载均衡调用非常简单,只需要如下两步:

  • 服务提供者只需要启动多个服务实例并注册到一个注册中心或是多个相关联的服务注册中心。
  • 服 务 消 费 者 直 接 通 过 调 用 被 @LoadBalanced 注 解 修 饰 过的RestTemplate来实现面向服务的接口调用。

这样,我们就可以将服务提供者的高可用以及服务消费者的负载均衡调用一起实现了。

RestTemplate核心方法

GET 请求

getForEntity

getForEntity函数。该方法返回的是ResponseEntity,该对象是Spring对HTTP请求响应的封装,其中主要存储了HTTP的几个重要元素,比如HTTP请求状态码的枚举对象httpStatus(也就是我们常说的404、500这些错误码)、在它的父类HttpEntity中还存储着HTTP请求的头信息对象HttpHeaders以及泛型类型的请求体对象。

getForObject

getForObject函数。该方法可以理解为对getForEntity的进一步封装,它通过HttpMessageConverterExtractor对HTTP的请求响应体body内容进行对象转换,实现请求直接返回包装好的对象内容。

POST 请求

postForEntity

postForEntity函数。该方法同GET请求中的getForEntity类似,会在调用后返回ResponseEntity对象,其中T为请求响应的body类型。

postForObject

postForObject 函数。该方法也跟 getForObject 的类型类似,它的作用是简化postForEntity的后续处理。通过直接将请求响应的body内容包装成对象来返回使用。

postForLocation

postForLocation函数。该方法实现了以POST请求提交资
源,并返回新资源的URI

PUT请求

在RestTemplate中,对PUT请求可以通过put方法进行调用实现。put函数为void类型,所以没有返回内容,也就没有其他函数定义的 responseType 参 数 , 除 此 之 外 的 其 他 传 入 参 数 定 义 与 用 法 与 postForObject 基本一致。

DELETE请求

在RestTemplate中,对DELETE请求可以通过delete方法进行调用实现,由于我们在进行REST请求时,通常都将DELETE请求的唯一标识拼接在url中,所以DELETE请求也不需要request的body信息。

源码分析

从@LoadBalanced 注解源码的注释中可以知道,该注解用来给RestTemplate 做标记,以使用负载均衡的客户端(LoadBalancerClient)来配置它。

LoadBalancerClient 是Spring Cloud中定义的一个接口:



public interface LoadBalancerClient {
    ServiceInstance choose(String serviceId);
    
    <T> T execute ( String serviceId,LoadBalancerRequest<T>
request)throws IOException;

     URI reconstructURI(ServiceInstance instance,URI original);
}



从该接口中,我们可以通过定义的抽象方法来了解客户端负载均
衡器中应具备的几种能力:

  • ServiceInstance choose(String serviceId):根据传入的服务名serviceId,从负载均衡器中挑选一个对应服务的实例。
  • T execute( String serviceId,LoadBalancerRequest request) throws IOException:使用从负载均衡器中挑选出的服务实例来执行请求内容。
  • URI reconstructURI(ServiceInstance instance,URI original):为系统构建一个合适的host:port形式的URI。在分布式系统中,我们使用逻辑上的服务名称作为host来构建URI(替代服务实例的host:port形式)进行请求。

类图关系

在这里插入图片描述

从类的命名上可初步判断 LoadBalancerAutoConfiguration 为实现客户端负载均衡器的自动化配置类。


@Configuration
@ConditionalOnClassRestTemplate.class@ConditionalOnBeanLoadBalancerClient.classpublic class LoadBalancerAutoConfiguration {
     @LoadBalanced
     @Autowired(required=falseprivate List<RestTemplate>
     restTemplates=Collections.emptyList();
     @Bean
     public SmartInitializingSingleton
     loadBalancedRestTemplateInitializer(
     final List<RestTemplateCustomizer> customizers){
     return new SmartInitializingSingleton(){
     @Override
     public void afterSingletonsInstantiated(){
         forRestTemplate restTemplate :LoadBalancerAutoConfiguration.this.restTemplates){
         forRestTemplateCustomizer customizer :customizers){
        customizer.customize(restTemplate);
}
}
}
};
}
@Bean
@ConditionalOnMissingBean
public RestTemplateCustomizer restTemplateCustomizer(
final LoadBalancerInterceptor loadBalancerInterceptor){
      return new RestTemplateCustomizer(){
@Override
public void customize(RestTemplate restTemplate){
    List<ClientHttpRequestInterceptor> list=new ArrayList<>(
      restTemplate.getInterceptors());
      list.add(loadBalancerInterceptor);
      restTemplate.setInterceptors(list);
}
};
}

@Bean
public LoadBalancerInterceptor ribbonInterceptor(
LoadBalancerClient loadBalancerClient){
     return new LoadBalancerInterceptor(loadBalancerClient);
}
}





从LoadBalancerAutoConfiguration类头上的注解可以知道,Ribbon实现的负载均衡自动化配置需要满足下面两个条件。

  • @ConditionalOnClass(RestTemplate.class):RestTemplate 类必须存在于当前工程的环境中。
  • @ConditionalOnBean ( LoadBalancerClient.class ) : 在 Spring 的Bean工程中必须有LoadBalancerClient的实现Bean。

在该自动化配置类中,主要做了下面三件事:

  • 创建了一个 LoadBalancerInterceptor 的 Bean,用于实现对客户端发起请求时进行拦截,以实现客户端负载均衡。
  • 创建了一个RestTemplateCustomizer的Bean,用于给RestTemplate增加LoadBalancerInterceptor拦截器。
  • 维护了一个被@LoadBalanced注解修饰的RestTemplate对象列表,并在这里进行初始化,通过调用RestTemplateCustomizer的实例来给需要客户端负载均衡的RestTemplate增加LoadBalancerInterceptor拦截器。

LoadBalancerInterceptor 拦截器将一个普通的RestTemplate变成客户端负载均衡流程:

当一个被@LoadBalanced注解修饰的RestTemplate对象向外发起HTTP请求时,会被LoadBalancerInterceptor类的intercept函数所拦截。由于我们在使用RestTemplate时采用了服务名作为host,所以直接从HttpRequest的URI对象中通过getHost()就可以拿到服务名,然后调用execute函数去根据服务名来选择实例并发起实际的请求。

LoadBalancerClient只是一个抽象的负载均衡器接口,实现类为org.springframework.cloud.netflix.ribbon 包下的RibbonLoadBalancerClient。在execute函数的实现中,第一步做的就是通过getServer根据传入的服务名serviceId去获得具体的服务实例。

在使用Ribbon实现负载均衡器的时候,实际使用的还是Ribbon中定义的 ILoadBalancer 接 口 的 实 现 , 自 动 化 配 置 会 采 用
ZoneAwareLoadBalancer的实例来实现客户端负载均衡。

与Eureka结合

当在Spring Cloud的应用中同时引入Spring Cloud Ribbon和Spring Cloud Eureka依赖时,会触发Eureka中实现的对Ribbon的自动化配置。这 时 ServerList 的 维 护 机 制 实 现 将 被com.netflix.niws.loadbalancer.DiscoveryEnabledNIWSServerList 的实例所覆盖,该实现会将服务清单列表交给Eureka的服务治理机制来进行维护;IPing的实现将被com.netflix.niws.loadbalancer.NIWSDiscoveryPing的实例所覆盖,该实现也将实例检查的任务交给了服务治理框架来进行维护。默认情况下,用于获取实例请求的ServerList接口实现将采用Spring Cloud Eureka 中 封 装 的org.springframework.cloud.netflix.ribbon.eureka.DomainExtractingServerList,其目的是为了让实例维护策略更加通用,所以将使用物理元数据来进行负载均衡,而不是使用原生的AWS AMI元数据。

由于Spring Cloud Ribbon默认实现了区域亲和策略,所以,我们可以通过Eureka实例的元数据配置来实现区域化的实例配置方案。

重试机制

由于Spring Cloud Eureka实现的服务治理机制强调了CAP原理中的AP,即可用性与可靠性,它与ZooKeeper这类强调CP(一致性、可靠性)的服务治理框架最大的区别就是,Eureka为了实现更高的服务可用性,牺牲了一定的一致性,在极端情况下它宁愿接受故障实例也不要丢掉“健康”实例,比如,当服务注册中心的网络发生故障断开时,由于所有的服务实例无法维持续约心跳,在强调 AP 的服务治理中将会把所有服务实例都剔除掉,而Eureka则会因为超过85%的实例丢失心跳而会触发保护机制,注册中心将会保留此时的所有节点,以实现服务间依然可以进行互相调用的场景,即使其中有部分故障节点,但这样做可以继续保障大多数的服务正常消费。

由于Spring Cloud Eureka在可用性与一致性上的取舍,不论是由于触发了保护机制还是服务剔除的延迟,引起服务调用到故障实例的时候,希望能够增强对这类问题的容错。所以,在实现服务调用的时候通常会加入一些重试机制。较高版本的Spring Cloud整合了Spring Retry来增强RestTemplate的重试能力,对于开发者来说只需通过简单的配置,原来那些通过RestTemplate 实现的服务访问就会自动根据配置来实现重试策略(相关配置可以查看官网)。

相关文章:

  • 清华DeepSeek深度探索与进阶指南
  • 扫描纸质文件转pdf---少页数+手机+电脑协作
  • 01. HarmonyOS应用开发实践与技术解析
  • 2025-3-3 二叉树的存储结构
  • Makefile
  • 【Java EE】JavaEE导读,探寻 JavaEE:解锁企业级开发的璀璨密码与进阶指南
  • 论文学习——The Hilti SLAM Challenge Dataset
  • iOS逆向工程概述与学习路线图
  • DeepSeek、Grok、ChatGPT4.5和Gemini四大AI模型深度解析:谁才是你的最佳助手
  • 704. 二分查找
  • 深入解析 I²C 与 SPI 协议:原理、时序及软件实现
  • Git强制覆盖分支:将任意分支完全恢复为main分支内容
  • IO进程线程
  • 2025华为OD机试真题目录【E卷+A卷+B卷+C卷+D卷】持续收录中...
  • 基于RK3588的重症监护信息系统应用解决方案
  • 深拷贝与浅拷贝
  • 微服务,服务治理nacos,负载均衡LOadBalancer,OpenFeign
  • Leetcode 662: 二叉树最大宽度
  • 大白话跨域问题的原理与多种解决方法的实现
  • 信息学奥赛一本通1009
  • 毗邻三市人均GDP全部超过20万元,苏锡常是怎样做到的?
  • 习近平出席俄罗斯总统举行的欢迎仪式
  • 陈丹燕:赤龙含珠
  • 8小时《大师与玛格丽特》:长度可以是特点,但不是价值标准
  • 41年轮回,从洛杉矶奔向洛杉矶,李宁故地重游再出发
  • 躺着玩手机真有意思,我“瞎”之前最喜欢了