彻底搞懂 Spring Cloud 前世今生 系列之 第⼀代 Spring Cloud 核⼼组件 — Hystrix熔断器

彻底搞懂 Spring Cloud 前世今生 系列之 第⼀代 Spring Cloud 核⼼组件 — Hystrix熔断器Hystrix 熔断器 属于 种容错机制 微服务中的雪崩效应 什么是微服务中的雪崩效应呢 微服务中 个请求可能需要多个微服务接 才能实现 会形成复杂的调 链路 扇 代表着该微服务被调 的次数 扇 说明该模块复 性好 扇出

大家好,我是讯享网,很高兴认识大家。

Hystrix熔断器

属于⼀种容错机制

微服务中的雪崩效应

什么是微服务中的雪崩效应呢?

微服务中,⼀个请求可能需要多个微服务接⼝才能实现,会形成复杂的调⽤链路。
在这里插入图片描述
讯享网
在这里插入图片描述

  • 扇⼊:代表着该微服务被调⽤的次数,扇⼊⼤,说明该模块复⽤性好
  • 扇出:该微服务调⽤其他微服务的个数,扇出⼤,说明业务逻辑复杂
    扇⼊⼤是⼀个好事,扇出⼤不⼀定是好事

在微服务架构中,⼀个应⽤可能会有多个微服务组成,微服务之间的数据交互通过远程过程调⽤完成。这就带来⼀个问题,假设微服务A调⽤微服务B和微服务C,微服务B和微服务C⼜调⽤其它的微服务,这就是所谓的“扇出”。如果扇出的链路上某个微服务的调⽤响应时间过⻓或者不可⽤,对微服务A的调⽤就会占⽤越来越多的系统资源,进⽽引起系统崩溃,所谓的“雪崩效应”。

如图中所示,最下游简历微服务响应时间过⻓,⼤量请求阻塞,⼤量线程不会释放,会导致服务器资源耗尽,最终导致上游服务甚⾄整个系统瘫痪。

雪崩效应解决⽅案

  • 服务熔断
    熔断机制是应对雪崩效应的⼀种微服务链路保护机制。我们在各种场景下都会接触到熔断这两个字。⾼压电路中,如果某个地⽅的电压过⾼,熔断器就会熔断,对电路进⾏保护。股票交易中,如果股票指数过⾼,也会采⽤熔断机制,暂停股票的交易。同样,在微服务架构中,熔断机制也是起着类似的作⽤。当扇出链路的某个微服务不可⽤或者响应时间太⻓时,熔断该节点微服务的调⽤,进⾏服务的降级,快速返回错误的响应信息。当检测到该节点微服务调⽤响应正常后,恢复调⽤链路。
    注意:
    • 服务熔断重点在“断”,切断对下游服务的调⽤
    • 服务熔断和服务降级往往是⼀起使⽤的,Hystrix就是这样。
  • 服务降级
    通俗讲就是整体资源不够⽤了,先将⼀些不关紧的服务停掉(调⽤我的时候,给你返回⼀个预留的值,也叫做兜底数据),待渡过难关⾼峰过去,再把那些服务打开。

    服务降级⼀般是从整体考虑,就是当某个服务熔断之后,服务器将不再被调⽤,此刻客户端可以⾃⼰准备⼀个本地的fallback回调,返回⼀个缺省值,这样做,虽然服务⽔平下降,但好⽍可⽤,⽐直接挂掉要强。

  • 服务限流
    服务降级是当服务出问题或者影响到核⼼流程的性能时,暂时将服务屏蔽掉,待⾼峰或者问题解决后再打开;但是有些场景并不能⽤服务降级来解决,⽐如秒杀业务这样的核⼼功能,这个时候可以结合服务限流来限制这些场景的并发/请求量

    限流措施也很多,⽐如

    • 限制总并发数(⽐如数据库连接池、线程池)
    • 限制瞬时并发数(如nginx限制瞬时并发连接数)
    • 限制时间窗⼝内的平均速率(如Guava的RateLimiter、nginx的limit_req模块,限制每秒的平均速率)
    • 限制远程接⼝调⽤速率、限制MQ的消费速率等

Hystrix简介

[来⾃官⽹] Hystrix(豪猪----->刺),宣⾔“defend your app”是由Netflix开源的⼀个延迟和容错库,⽤于隔离访问远程系统、服务或者第三⽅库,防⽌级联失败,从⽽提升系统的可⽤性与容错性。
Hystrix主要通过以下⼏点实现延迟和容错。

  • 包裹请求:使⽤HystrixCommand包裹对依赖的调⽤逻辑。 ⾃动投递微服务⽅法
    (@HystrixCommand 添加Hystrix控制) ——调⽤简历微服务
  • 跳闸机制:当某服务的错误率超过⼀定的阈值时,Hystrix可以跳闸,停⽌请求该服务⼀段时间。
  • 资源隔离:Hystrix为每个依赖都维护了⼀个⼩型的线程池(舱壁模式)(或者信号量)。如果该线程池已满, 发往该依赖的请求就被⽴即拒绝,⽽不是排队等待,从⽽加速失败判定。
  • 监控:Hystrix可以近乎实时地监控运⾏指标和配置的变化,例如成功、失败、超时、以及被拒绝的请求等。
  • 回退机制:当请求失败、超时、被拒绝,或当断路器打开时,执⾏回退逻辑。回退逻辑由开发⼈员⾃⾏提供,例如返回⼀个缺省值。
  • ⾃我修复:断路器打开⼀段时间后,会⾃动进⼊“半开”状态。

Hystrix舱壁模式(线程池隔离策略)

在这里插入图片描述
如果不进⾏任何设置,所有熔断⽅法使⽤⼀个Hystrix线程池(10个线程),那么这样的话会导致问题,这个问题并不是扇出链路微服务不可⽤导致的,⽽是我们的线程机制导致的,如果⽅法A的请求把10个线程都⽤了,⽅法2请求处理的时候压根都没法去访问B,因为没有线程可⽤,并不是B服务不可⽤。
在这里插入图片描述
为了避免问题服务请求过多导致正常服务⽆法访问,Hystrix 不是采⽤增加线程数,⽽是单独的为每⼀个控制⽅法创建⼀个线程池的⽅式,这种模式叫做“舱壁模式",也是线程隔离的⼿段。

Hystrix⼯作流程与⾼级应⽤

在这里插入图片描述

  1. 当调⽤出现问题时,开启⼀个时间窗(10s)
  2. 在这个时间窗内,统计调⽤次数是否达到最⼩请求数?
    • 如果没有达到,则重置统计信息,回到第1步
    • 如果达到了,则统计失败的请求数占所有请求数的百分⽐,是否达到阈值?
    • 如果达到,则跳闸(不再请求对应服务)
    • 如果没有达到,则重置统计信息,回到第1步 3)
  3. 如果跳闸,则会开启⼀个活动窗⼝(默认5s),每隔5s,Hystrix会让⼀个请求通过,到达那个问题服务,看 是否调⽤成功,如果成功,重置断路器回到第1步,如果失败,回到第3步
/ * 8秒钟内,请求次数达到2个,并且失败率在50%以上,就跳闸 * 跳闸后活动窗⼝设置为3s */ @HystrixCommand( commandProperties = { 
    @HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds",value = "8000"), @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "2"), @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "50"), @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "3000") ) 

讯享网

我们上述通过注解进⾏的配置也可以配置在配置⽂件中

讯享网# 配置熔断策略: hystrix: command: default: circuitBreaker: # 强制打开熔断器,如果该属性设置为true,强制断路器进⼊打开状态,将会拒绝所有的请求。 默认false关闭的 forceOpen: false # 触发熔断错误⽐例阈值,默认值50% errorThresholdPercentage: 50 # 熔断后休眠时⻓,默认值5秒 sleepWindowInMilliseconds: 3000  # 熔断触发最⼩请求次数,默认值是20 requestVolumeThreshold: 2  execution: isolation: thread: # 熔断超时设置,默认为1秒 timeoutInMilliseconds: 2000 

基于springboot的健康检查观察跳闸状态(⾃动投递微服务暴露健康检查细节)

# springboot中暴露健康检查等断点接⼝ management: endpoints: web: exposure: include: "*" # 暴露健康接⼝的细节 endpoint: health: show-details: always 

访问健康检查接⼝:http://localhost:8090/actuator/health

  • hystrix正常⼯作状态
    在这里插入图片描述
  • 跳闸状态
    在这里插入图片描述
  • 活动窗⼝内⾃我修复
    在这里插入图片描述

Hystrix Dashboard断路监控仪表盘

正常状态是UP,跳闸是⼀种状态CIRCUIT_OPEN,可以通过/health查看,前提是⼯程中需要引⼊SpringBoot的actuator(健康监控),它提供了很多监控所需的接⼝,可以对应⽤系统进⾏配置查看、相关功能统计等。

如果我们想看到Hystrix相关数据,⽐如有多少请求、多少成功、多少失败、多少降级等,那么引⼊SpringBoot健康监控之后,访问/actuator/hystrix.stream接⼝可以获取到监控的⽂字信息,但是不直观,所以Hystrix官⽅还提供了基于图形化的DashBoard(仪表板)监控平 台。Hystrix仪表板可以显示每个断路器(被@HystrixCommand注解的⽅法)的状态。
在这里插入图片描述

  1. 新建⼀个监控服务⼯程,导⼊依赖
讯享网 <!--hystrix--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency> <!--hystrix 仪表盘--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> 
  1. 启动类添加@EnableHystrixDashboard激活仪表盘
    @SpringBootApplication @EnableHystrixDashboard // 开启hystrix dashboard public class HystrixDashboardApplication9000 { 
          public static void main(String[] args) { 
          SpringApplication.run(HystrixDashboardApplication.class, args); } } 
  2. application.yml
讯享网server: port: 9000 Spring: application: name: learn-cloud-hystrix-dashboard eureka: client: serviceUrl: # eureka server的路径 defaultZone: http://learncloudeurekaservera:8761/eureka/,http://learncoudeurekaserverb:8762/eureka/ #把 eureka 集群中的所有 url 都填写了进来,也可以只写⼀台,因为各个 eurekaserver 可以同步注册表 instance: #使⽤ip注册,否则会使⽤主机名注册了(此处考虑到对⽼版本的兼容,新版本经过实验都是ip) prefer-ip-address: true #⾃定义实例显示格式,加上版本号,便于多版本管理,注意是ip-address,早期版本是ipAddress instance-id: ${ 
   spring.cloud.client.ipaddress}:${ 
   spring.application.name}:${ 
   server.port}:@project.version@ 
  1. 在被监测的微服务中注册监控servlet(⾃动投递微服务,监控数据就是来⾃于这个微服务)
    在这里插入图片描述

    被监控微服务发布之后,可以直接访问监控servlet,但是得到的数据并不直观,后期可以结合仪表盘更友好的展示
    在这里插入图片描述

  2. 访问测试http://localhost:9000/hystrix
    在这里插入图片描述
    输⼊监控的微服务端点地址,展示监控的详细数据,⽐如监控服务消费者http://localhost:8090/actuator/hystrix.stream
    在这里插入图片描述
    百分⽐,10s内错误请求百分⽐
    实⼼圆:
    • ⼤⼩:代表请求流量的⼤⼩,流量越⼤球越⼤
    • 颜⾊:代表请求处理的健康状态,从绿⾊到红⾊递减,绿⾊代表健康,红⾊就代表很不健康

Hystrix Turbine聚合监控

之前,我们针对的是⼀个微服务实例的Hystrix数据查询分析,在微服务架构下,⼀个微服务的实例往往是多个(集群化)
⽐如⾃动投递微服务
实例1(hystrix) ip1:port1/actuator/hystrix.stream
实例2(hystrix) ip2:port2/actuator/hystrix.stream
实例3(hystrix) ip3:port3/actuator/hystrix.stream
按照已有的⽅法,我们就可以结合dashboard仪表盘每次输⼊⼀个监控数据流url,进去查看

思考:微服务架构下,⼀个微服务往往部署多个实例,如果每次只能查看单个实例的监控,就需要经常切换很不⽅便,在这样的场景下,我们可以使⽤ Hystrix Turbine 进⾏聚合监控,它可以把相关微服务的监控数据聚合在⼀起,便于查看。
在这里插入图片描述
Turbine服务搭建

  1. 新建项⽬learn-cloud-hystrix-turbine-9001,引⼊依赖坐标
    <dependencies> <!--hystrix turbine聚合监控--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-turbine</artifactId> </dependency> <!-- 引⼊eureka客户端的两个原因 1、微服务架构下的服务都尽量注册到服务中⼼去,便于统⼀管理 2、后续在当前turbine项⽬中我们需要配置turbine聚合的服务,⽐如,我们希望聚合 learn-service-autodeliver这个服务的各个实例的hystrix数据流,那随后 我们就需要在application.yml⽂件中配置这个服务名,那么turbine获取服务下具 体实例的数据流的 时候需要ip和端⼝等实例信息,那么怎么根据服务名称获取到这些信息呢? 当然可以从eureka服务注册中⼼获取 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eurekaclient</artifactId> </dependency> </dependencies> 
  2. 将需要进⾏Hystrix监控的多个微服务配置起来,在⼯程application.yml中开启Turbine及进⾏相关配置
    讯享网server: port: 9001 Spring: application: name: learn-cloud-hystrix-turbine eureka: client: serviceUrl: # eureka server的路径 #把 eureka 集群中的所有 url 都填写了进来,也可以只写⼀台,因为各个 eurekaserver 可以同步注册表 defaultZone: http://learnloudeurekaservera:8761/eureka/,http://learncloudeurekaserverb:8762/eureka/ instance: #使⽤ip注册,否则会使⽤主机名注册了(此处考虑到对⽼版本的兼容,新版本经过实验都是ip) prefer-ip-address: true #⾃定义实例显示格式,加上版本号,便于多版本管理,注意是ip-address,早期版本是ipAddress instance-id: ${ 
         spring.cloud.client.ipaddress}:${ 
         spring.application.name}:${ 
         server.port}:@project.version@ #turbine配置 turbine: # appCofing配置需要聚合的服务名称,⽐如这⾥聚合⾃动投递微服务的hystrix监控数据 # 如果要聚合多个微服务的监控数据,那么可以使⽤英⽂逗号拼接,⽐如 a,b,c appConfig: learn-service-autodeliver clusterNameExpression: "'default'" # 集群默认名称 
  3. 在当前项⽬启动类上添加注解@EnableTurbine,开启仪表盘以及Turbine聚合
    在这里插入图片描述
    4.浏览器访问Turbine项⽬,http://localhost:9001/turbine.stream,就可以看到监控数据了
    在这里插入图片描述
    我们通过dashboard的⻚⾯查看数据更直观,把刚才的地址输⼊dashboard地址栏
    在这里插入图片描述
    在这里插入图片描述

Hystrix核⼼源码剖析

  • @EnableCircuitBreaker注解激活熔断器
    在这里插入图片描述
  • 查看EnableCircuitBreakerImportSelector类
    在这里插入图片描述
  • 继续关注⽗类 SpringFactoryImportSelector
    在这里插入图片描述
    在这里插入图片描述
  • spring.factories⽂件内容如下
    在这里插入图片描述
  • 会注⼊org.springframework.cloud.netflix.hystrix.HystrixCircuitBreakerConfiguration
    在这里插入图片描述
  • 关注切⾯:com.netflix.hystrix.contrib.javanica.aop.aspectj.HystrixCommandAspect
    在这里插入图片描述
  • 重点分析环绕通知⽅法
    在这里插入图片描述
    GenericCommand中根据元数据信息重写了两个很核⼼的⽅法,⼀个是run⽅法封装了对原始⽬标⽅法的调⽤,另外⼀个是getFallBack⽅法,它封装了对回退⽅法的调⽤
    另外,在GenericCommand的上层类构造函数中会完成资源的初始化,⽐如线程池
    GenericCommand —>AbstractHystrixCommand—>HystrixCommand—>AbstractCommand
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
  • 接下来回到环绕通知⽅法那张截图
    在这里插入图片描述
  • 进⼊execute执⾏这⾥
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
  • 另外,我们观察,GenericCommand⽅法中根据元数据信息等重写了run⽅法(对⽬标⽅法的调⽤),getFallback⽅法(对回退⽅法的调⽤),在RxJava处理过程中会完成对这两个⽅法的调⽤。
    在这里插入图片描述
    在这里插入图片描述
小讯
上一篇 2025-04-11 10:34
下一篇 2025-03-30 18:55

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/125510.html