背景
在分布式环境下,服务之间有大量的依赖,单个依赖故障时的容灾是个很重要的话题。
伴随着业务复杂性的提高,系统的不断拆分,一个面向用户端的API,其内部的RPC调用层层嵌套,调用链条可能会非常长。这会造成以下问题:API接口可用性降低。
引用Hystrix官方例子,如果一个服务依赖30个子服务,子服务都是99.99%的可用性,那么该服务可用性为99.99%的30次方 = 99.7%,即0.3%的失败率。从请求数量上讲,一亿次请求中如果有0.3%的失败,失败次数是300万。从时间上讲,上述服务相当于每个月有2个小时的不可用时间。
名称解释
服务熔断
类似现实世界中的“保险丝”,当某个异常条件被触发,直接熔断整个服务,而不是一直等到此服务超时。 熔断的触发条件可以依据不同的场景有所不同,比如统计一个时间窗口内失败的调用次数。
保证每个服务互不影响,使用信号量和线程池方式
服务降级
一般是从整体负荷考虑。就是当某个服务熔断之后,服务器将不再被调用,此时客户端可以自己准备一个本地的fallback回调,返回一个缺省值。 这样做虽然服务水平下降,但好歹可用,比直接挂掉要强,当然这也要看适合的业务场景。
当服务不可用的时候,不会被等待,直接返回一个友好的提示
服务隔离
每个服务接口互不影响,服务隔离有两种实现方式线程池方式、计数器。当服务产生堆积的时候,对服务实现保护功能。
当服务器达到最大的承受能的之后,直接拒绝访问服务,最会调用服务降级方法,返回友好提示。目的是保证服务不被宕机掉
Hystrix是什么
介绍
Hystrix 是有 Netflix 开源的一个针对分布式系统容错处理的开源组件。Hystrix单词意为"豪猪”,浑身有刺来保护自己,Hystrix 就是这样一个用来捍卫应用程序健康的利器。
Hystrix 是一个延迟和容错库,旨在隔离远程系统、服务和第三方库,阻止级联故障,在复杂的分布式系统中实现恢复能力。
设计目标
- 通过客户端库对延迟和故障进行保护和控制。
- 在一个复杂的分布式系统中停止级联故障。
- 快速失败和迅速恢复。
- 在合理的情况下回退和优雅地降级。
- 开启近实时监控、告警和操作控制。
做了什么事
- 资源隔离:包括线程池隔离和信号量隔离,避免某个依赖出现问题会影响到其他依赖。
- 断路器:当请求失败率达到一定的阈值时,会打开断路器开关,直接拒绝后续的请求,并且具有弹性机制,在后端服务恢复后,会自动关闭断路器开关。
- 降级回退:当断路器开关被打开,服务调用超时/异常,或者资源不足(线程、信号量)会进入指定的fallback降级方法。
- 请求结果缓存:hystrix实现了一个内部缓存机制,可以将请求结果进行缓存,那么对于相同的请求则会直接走缓存而不用请求后端服务。
- 请求合并:可以实现将一段时间内的请求合并,然后只对后端服务发送一次请求。
核心概念
资源隔离
Hystrix的资源隔离策略有两种,分别为:线程池和信号量。
线程池隔离:为每一个依赖创建一个线程池来处理来自该依赖的请求,不同的依赖线程池相互隔离,就算依赖A出故障,导致线程池资源被耗尽,也不会影响其他依赖的线程池资源。
优点:支持排队和超时,支持异步调用。
缺点:线程的创建一个调度会造成一定的性能开销。
信号量隔离:初始化信号量currentCount=0,每进来一个请求需要先将currentCount自增,再判断currentCount的值是否小于系统最大信号量,小于则继续执行,大于则直接返回,拒绝请求。
优点:轻量,无额外的开销,只是一个简单的计数器。
缺点:不支持任务排队和主动超时,不支持异步调用。
隔离方式 | 是否支持超时 | 是否支持熔断 | 隔离原理 | 是否异步调用 | 资源消耗 | 适用场景 |
---|---|---|---|---|---|---|
线程池隔离 | 支持,可直接返回 | 支持,当线程池到达maxSize后,再请求会触发fallback接口进行熔断 | 每个服务单独用线程池,请求线程与转发处理线程不是同一个 | 可以是异步,也可以是同步。看调用的方法 | 大,大量线程的上下文切换,容易造成机器负载高 | 适合耗时较长的接口场景,因为线程池模式的请求线程与实际转发线程不是同一个,所以可以保证容器有足够的线程来处理新的请求。比如接口处理逻辑复杂,且与第三方中间件有交互。 |
信号量隔离 | 不支持,如果阻塞,只能通过调用协议(如:socket超时才能返回) | 支持,当信号量达到maxConcurrentRequests后。再请求会触发fallback | 通过信号量的计数器,请求线程与转发处理线程是同一个 | 同步调用,不支持异步 | 小,只是个计数器 | 适合能快速响应的接口场景,不适合一些耗时较长的接口场景,因为信号量模式下的请求线程与转发处理线程是同一个,如果接口耗时过长有可能会占满容器的线程数。 |
断路器
断路器工作原理如图:
Hystrix是基于滚筒式来处理,每一秒会产生一个buckets,每产生一个新的buckets就会移除一个最老的buckets,默认是10秒一个窗口。buckets在内存中就是一种数据结构,每个buckets会记录Metrics的相关数据,比如成功、失败、超时、拒绝。
当一个HystrixCommand进来后,会先通过allowRequest()方法判断是否允许通过该次请求,allowRequest()方法会通过isOpen判断断路器是否打开。断路器关闭,则允许通过该次请求;断路器打开,则会判断是否过了睡眠周期。没有过睡眠周期则返回false,拒绝通过该次请求,过了睡眠周期则会尝试放行。
isOpen()方法会按照(failure) / (success+failure)公式计算出失败率,如果失败率大于阈值,则会触发熔断。公式中的成功、失败的数据就来源于每10秒中一个窗口的滚筒数据。
对于一个依赖调用,要么调用成功,要么调用失败(包括异常、超时、拒绝),这些调用结果都会记录到buckets中。对于调用成功结果来说,还会判断断路器开关是否打开,如果是打开状态的话,则会关闭断路器并重置相关的计数器。
降级回退
降级,通常指事务高峰期,为了保证核心服务正常运行,需要停掉一些不太重要的业务,或者某些服务不可用时,执行备用逻辑从故障服务中快速失败或快速返回,以保障主体业务不受影响。
Hystrix提供的降级主要是为了容错,保证当前服务不受依赖服务故障的影响,从而提高服务的健壮性。
进入降级逻辑的情况
- 断路器打开
- 线程池/信号量资源不足
- 执行依赖调用超时
- 执行依赖调用异常
请求结果缓存
实际应用场景很少,不做介绍,如图:
请求合并
实际应用场景很少,不做介绍,如图:
快速开始
引入相关依赖
引入Hystrix包,如下所示:
<dependency> |
启用断路器模式
开启断路器注解,在启动类添加@EnableHystrix注解。
@SpringBootApplication |
增加@HystrixCommand和降级fallback
在业务代码中增加@HystrixCommand和降级fallback
@HystrixCommand(fallbackMethod="defaultUser") |