面试系列之-Spring Cloud Hystrix

2023-11-20 15:00:30 浏览数 (2)

Hystrix的作用
服务降级

服务降级是从整个系统的负荷情况出发和考虑的,对某些负荷会比较高的情况,为了预防某些功能(业务场景)出现负荷过载或者响应慢的情况,在其内部暂时舍弃对一些非核心的接口和数据的请求,而直接返回一个提前准备好的fallback(退路)错误处理信息。这样虽然提供的是一个有损的服务,但却保证了整个系统的稳定性和可用性。

服务熔断

服务熔断的作用类似于我们家用的保险丝,当某服务出现不可用或响应超时的情况时,为了防止整个系统出现雪崩,暂时停止对该服务的调用。

熔断机制是应对雪崩效应的一种微服务链路保护机制。

当扇出链路的某个微服务不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回"错误"的响应信息。当检测到该节点微服务调用响应正常后恢复调用链路。在SpringCloud框架里熔断机制通过Hystrix实现。Hystrix会监控微服务间调用的状况,当失败的调用到一定阈值,缺省是5秒内20次调用失败就会启动熔断机制。熔断机制的注解是@HystrixCommand。

服务熔断的状态

熔断有三种状态:

  1. Closed:关闭状态,所有请求都正常访问。
  2. Open:打开状态,所有请求都会被降级。Hystix会对请求情况计数,当一定时间内失败请求百分比达到阈值,则触发熔断,断路器会完全打开。默认失败比例的阈值是50%,请求次数最少不低于20次。
  3. Half Open:半开状态,open状态不是永久的,打开后会进入休眠时间(默认是5S)。随后断路器会自动进入半开状态。此时会释放部分请求通过,若这些请求都是健康的,则会完全关闭断路器,否则继续保持打开,再次进行休眠计时
服务熔断的原理

统计用户在指定的时间范围之内的请求总数达到指定的数量之后,如果不健康的请求(超时、异常)占总请求数量的百分比达到了指定的阈值之后,就会触发熔断。触发熔断,断路器就会打开(open),此时所有请求都不能通过。在指定时间之后,断路器会恢复到半开状态(half open),会允许少量请求通过,如果这些请求都是健康的,那么断路器会回到关闭状态(close).如果这些请求还是失败的请求,断路器还是恢复到打开的状态(open);

当断路器的开关为关闭时(对应图中的绿色),每次请求进来都是成功的,当后端服务出现问题,请求出现的错误数达到一定的阈值,则会触发断路器为打开状态(对应图中的红色),在断路器为打开状态时,进来的所有请求都会被拒绝,当然也不是一直会拒绝请求,而是弹性的,过了特定的时间后,断路器会进入半打开状态(对应图中的黄色),这是会让一部分请求通过进行尝试,如果尝试还是有问题,则继续进入打开状态,如果尝试没有问题了,则会进入关闭状态;

熔断和降级的区别

共同点

为了系统的稳定性,防止因为个别微服务的不可用而拖死整个系统服务

提高用户的可用性,保证用户在访问过程中一定能得到有效结果

粒度上,都是服务级别的粒度,某些情况下,也有更细的粒度,如数据的持久层,只允许查询,不允许增删改

不同点

管理目标层次不一样,服务熔断是一个框架层次的处理,服务降级是业务层次的处理

实现方式不一样,服务熔断一般是自我熔断恢复,服务降级相当于人工控制

触发原因不同 服务熔断一般是某个服务(下游服务)故障引起,而服务降级一般是从整体负荷考虑,主动降级;

服务熔断是应对系统服务雪崩的一种保险措施,给出的一种特殊降级措施。而服务降级则是对系统整体资源的合理分配以应对压力

服务限流
接近实时的监控

如果微服务中一台服务器宕机,导致大量访问得不到结果,或者如果某个接口出现异常,则需要处理,比如熔断、限流、降级。例如秒杀抢购会出现从正常访问降为无法访问请求结果缓存,hystrix实现了一个内部缓存机制,可以将请求结果进行缓存,那么对于相同的请求则会直接走缓存而不用请求后端服务;

请求合并

可以实现将一段时间内的请求合并,然后只对后端服务发送一次请求;

Hystrix能解决的问题
服务降级

在高并发情况下,防止用户一直等待(返回一个友好的提示,直接给客户端,不会去处理请求,调用fallBack本地方法),目的是为了用户体验,秒杀----当前请求人数过多,请稍后重试

服务熔断

熔断机制目的为了保护服务,在高并发的情况下,如果请求达到一定极限(可以自己设置阔值)如果流量超出了设置阈值,让后直接拒绝访问,保护当前服务。使用服务降级方式返回一个友好提示,服务熔断和服务降级一起使用

服务隔离机制

因为默认情况下,只有一个线程池会维护所有的服务接口,如果大量的请求访问同一个接口,达到tomcat 线程池默认极限,可能会导致其他服务无法访问;解决服务雪崩效应:1. 使用服务隔离机制(线程池方式和信号量) 2. 使用线程池方式实现

线程池隔离

相当于每个接口(服务)都有自己独立的线程池,因为每个线程池互不影响,这样的话就可以解决服务雪崩效应;线程池隔离会为每一个依赖(每一个服务提供者)创建一个线程池来处理来自该依赖的请求,不同的依赖线程池相互隔离,就算依赖A出故障,导致线程池资源被耗尽,也不会影响其他依赖的线程池资源;

  • 优点:支持排队和超时,支持异步调用
  • 缺点:线程的创建一个调度会造成一定的性能开销
  • 适用场景:适合耗时较长的接口场景,比如接口处理逻辑复杂,且与第三方中间件有交互,因为线程池模式的请求线程与实际转发线程不是同一个,所以可以保证容器有足够的线程来处理新的请求
信号量隔离

使用一个原子计数器(或信号量)来记录当前有多少个线程在运行,当请求进来时先判断计数器的数值,若超过设置的最大线程个数则拒绝该请求,若不超过则通行,这时候计数器 1,请求返 回成功后计数器-1;初始化信号量currentCount=0,每进来一个请求需要先将currentCount自增,再判断currentCount的值是否小于系统最大信号量,小于则继续执行,大于则直接返回,拒绝请求;

  • 优点:轻量,无额外的开销,只是一个简单的计数器
  • 缺点:不支持任务排队和主动超时;不支持异步调用
  • 适用场景:适合能快速响应的接口场景,不适合一些耗时较长的接口场景,因为信号量模式下的请求线程与转发处理线程是同一个,如果接口耗时过长有可能会占满容器的线程数

隔离方式

是否支持超时

是否支持熔断

隔离原理

是否异步调用

资源消耗

线程池隔离

支持,可直接返回

支持,当线程池到达maxSize后,再请求会触发fallback接口进行熔断

每个服务单独用线程池,请求线程与转发处理线程不是同一个

可以是异步,也可以是同步。看调用的方法

大,大量线程的上下文切换,容易造成机器负载高

信号量隔离

不支持,如果阻塞,只能通过调用协议(如:socket超时才能返回)

支持,当信号量达到maxConcurrentRequests后。再请求会触发fallback

通过信号量的计数器,请求线程与转发处理线程是同一个

同步调用,不支持异步

小,只是个计数器

限流

高并发的流量涌入进来,比如说突然间一秒钟100万QPS,废掉了,10万QPS进入系统,其他90万QPS被拒绝了

Hystrix的熔断设计
  • 熔断请求判断机制算法:使用无锁循环队列计数,每个熔断器默认维护10个bucket,每1秒一个bucket,每个blucket记录请求的成功、失败、超时、拒绝的状态,默认错误超过50%且10秒内超过20个请求进行中断拦截。
  • 熔断恢复:对于被熔断的请求,每隔5s允许部分请求通过,若请求都是健康的(RT<250ms)则对请求健康恢复。
  • 熔断报警:对于熔断的请求打日志,异常请求超过某些设定则报警
Hystrix的隔离设计
  • 线程池隔离模式:使用一个线程池来存储当前的请求,线程池对请求作处理,设置任务返回处理超时时间,堆积的请求堆积入线程池队列。这种方式需要为每个依赖的服务申请线程池,有一定的资源消耗,好处是可以应对突发流量(流量洪峰来临时,处理不完可将数据存储到线程池队里慢慢处理)
  • 信号量隔离模式:使用一个原子计数器(或信号量)来记录当前有多少个线程在运行,请求来先判断计数器的数值,若超过设置的最大线程个数则丢弃改类型的新请求,若不超过则执行计数操作请求来计数器 1,请求返回计数器-1。这种方式是严格的控制线程且立即返回模式,无法应对突发流量(流量洪峰来临时,处理的线程超过数量,其他的请求会直接返回,不继续去请求依赖的服务)
Hystrix超时机制设计
  • 等待超时:在任务入队列时设置任务入队列时间,并判断队头的任务入队列时间是否大于超时时间,超过则丢弃任务
  • 运行超时:直接可使用线程池提供的get方法
Hystrix断路器机制原理

断路器很好理解,当 Hystrix Command 请求后端服务失败数量超过一定比例(默认 50%),断路器会切换到开路状态(Open),这时所有请求会直接失败而不会发送到后端服务。断路器保持在开路状态一段时间后(默认 5 秒),自动切换到半开路状态(HALF-OPEN)。这时会判断下一次请求的返回情况,如果请求成功,断路器切回闭路状态(CLOSED),否则重新切换到开路状态(OPEN)。Hystrix 的断路器就像我们家庭电路中的保险丝,一旦后端服务不可用,断路器会直接切断请求链,避免发送大量无效请求影响系统吞吐量,并且断路器有自我检测并恢复的能力;

Hystrix工作流程
  1. 构造Hystrix命令:构造一个HystrixCommand或HystrixObservableCommand对象,用于封装请求并在构造方法中配置请求被执行需要的参数;
  2. 执行Hystrix命令:根据上文中提供的4种方式执行命令;
  3. 判断是否缓存了响应:如果你为命令启用了请求缓存,并且在缓存中命中了可用请求的响应,则缓存的响应将立即以可观察到的形式返回;
  4. 判断熔断电路是否打开:当执行命令时,Hystrix将与断路器一起检查熔断电路是否打开。如果熔断电路打开,那么Hystrix将不执行命令并回退。如果熔断电路关闭,则继续执行,检查是否有可用的容量来运行命令;
  5. 线程池、队列、信号量是否已满:如果与命令关联的线程池和队列(或信号量,如果不在线程中运行)已满,那么Hystrix将不执行命令,执行逻辑跳转到第7步;
  6. 计算电路健康状态:执行HystrixObservableCommand.construct或HystrixCommand.run方法,Hystrix向断路器报告成功、失败、拒绝或超时,如果执行逻辑失败或者超,则执行逻辑跳转第7步;否则执行逻辑跳转到第8步;
  7. 回退:Hystrix试图恢复你的回滚命令,并执行回退逻辑或者fallback备用逻辑;
  8. 返回成功的响应如果Hystrix命令成功,它将以可观察到的形式返回响应给调用者;

简单来说:对于一次依赖调用,会被封装在一个HystrixCommand对象中,执行时会判断断路器开关是否打开,如果断路器打开,则进入getFallback()降级逻辑;如果断路器关闭,则判断线程池/信号量资源是否已满,如果资源满了,则进入getFallback()降级逻辑;如果没满,则执行run()方法。再判断执行run()方法是否超时,超时则进入getFallback()降级逻辑,run()方法执行失败,则进入getFallback()降级逻辑,执行成功则报告Metrics。Metrics中的数据包括执行成功、超时、失败等情况的数据,Hystrix会计算一个断路器的健康值,也就是失败率,当失败率超过阈值后则会触发断路器开关打开;

getFallback()逻辑为:如果没有实现fallback()方法,则直接抛出异常,另外fallback降级也是需要资源的,在fallback时需要获取一个针对fallback的信号量,只有获取成功才能fallback,获取信号量失败,则抛出异常,获取信号量成功,才会执行fallback方法并且会响应fallback方法中的内容;

0 人点赞