目录
-
定义
Hystrix是一个应用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时,异常等。Hystrix能够保证在一个依赖出问题的情况下,不会导致整个体系服务失败,避免级联故障,以提高分布式系统的弹性。
-
目的
- 对来自依赖的延迟和故障进行防护和控制(通常是通过网络访问的依赖)
- 阻止故障的连锁反应(服务雪崩,见服务雪崩与熔断)
- 快速失败并迅速恢复
- 回退并优雅降级
- 提供近实时的监控与告警
-
实现原理
- 使用命令模式将所有对外部服务(或依赖关系)的调用包装在HystrixCommand或HystrixObservableCommand对象中,并将该对象放在单独的线程中执行;
- 每个依赖都维护着一个线程池(或信号量),线程池被耗尽则拒绝请求(而不是让请求排队)。
- 记录请求成功,失败,超时和线程拒绝。
- 服务错误百分比超过了阈值,熔断器开关自动打开,一段时间内停止对该服务的所有请求。
- 请求失败,被拒绝,超时或熔断时执行降级逻辑。
- 近实时地监控指标和配置的修改。
-
工作流程
- 构造一个 HystrixCommand或HystrixObservableCommand对象,用于封装请求,并在构造方法配置请求被执行需要的参数
- 执行命令
- execute()
- 以同步堵塞方式执行run(),只支持接收一个值对象
- hystrix会从线程池中取一个线程来执行run(),并等待返回值。
- queue()
- 以异步非阻塞方式执行run(),只支持接收一个值对象。
- 调用queue()就直接返回一个Future对象。可通过 Future.get()拿到run()的返回结果,但Future.get()是阻塞执行的。若执行成功,Future.get()返回单个返回值。当执行失败时,如果没有重写fallback,Future.get()抛出异常。
- observe()
- 事件注册前执行run()/construct(),支持接收多个值对象,取决于发射源。
- 会返回一个hot Observable,也就是说,调用observe()自动触发执行run()/construct(),无论是否存在订阅者。
- 如果继承的是HystrixCommand,hystrix会从线程池中取一个线程以非阻塞方式执行run();如果继承的是HystrixObservableCommand,将以调用线程阻塞执行construct()。
- 使用方法:
- 调用observe()会返回一个Observable对象
- 调用这个Observable对象的subscribe()方法完成事件注册,从而获取结果
- toObservable()
- 事件注册后执行run()/construct(),支持接收多个值对象,取决于发射源。
- 会返回一个cold Observable,也就是说,调用toObservable()不会立即触发执行run()/construct(),必须有订阅者订阅Observable时才会执行。
- 如果继承的是HystrixCommand,hystrix会从线程池中取一个线程以非阻塞方式执行run(),调用线程不必等待run();如果继承的是HystrixObservableCommand,将以调用线程堵塞执行construct(),调用线程需等待construct()执行完才能继续往下走。
- 需注意的是,HystrixCommand也支持toObservable()和observe(),但是即使将HystrixCommand转换成Observable,它也只能发射一个值对象。只有HystrixObservableCommand才支持发射多个值对象。
- execute()
- 判断是否使用缓存响应请求,若启用了缓存,且缓存可用,直接使用缓存响应请求。Hystrix支持请求缓存,但需要用户自定义启动
- 判断熔断器是否打开,如果打开,跳到Fallback
- 判断线程池/队列/信号量是否已满,已满则跳到Fallback
- 执行HystrixObservableCommand.construct()或HystrixCommand.run(),如果执行失败或者超时,跳到Fallback;否则,直接返回请求响应
- 统计熔断器监控指标
- 走Fallback备用逻辑
- 返回请求响应
-
容错机制
-
资源隔离
-
资源隔离,就是将多个依赖服务的调用分别隔离到各个资源的内部,避免因为依赖服务的失败或者延迟,导致服务所有的线程资源花费在这个伤害,继而导致服务崩塌。
-
-
- hystrix中主要有两种资源隔离的技术:线程池隔离和信号量隔离。
-
线程池隔离技术
-
线程池隔离技术,是用自己的线程去调用web容器,控制web容器中线程的执行,而非web容器本身的线程。出现问题会抛出异常,在web容器的执行线程中可以捕获到,然后做进一步的处理。
-
-
-
- 使用场景
-
-
大部分的场景下其实都适合用这种技术,对于依赖服务的调用以及访问;能解决timeout的场景,可以避免调用线程阻塞住。
-
-
-
信号量隔离技术
-
-
信号量隔离技术是直接让web容器的线程直接去调用依赖服务,出现问题直接返回。
-
-
-
- 使用场景
- 通常是针对超大并发量的场景,每个服务实例的QPS都非常高,如果用线程池,可能撑不住那么高的并发,如果要撑住,可能要耗费大量的线程资源,那么就是用信号量,来进行限流保护。
- 适合访问不依赖于外部服务,而只是访问内部的一些复杂的业务逻辑,由于是内部访问,不存在timeout的问题,适合比较复杂逻辑的业务代码,防止大量的线程被这些逻辑给卡死,影响系统的稳定性。
- 使用场景
-
-
服务熔断
-
服务熔断简介(见服务雪崩与熔断)
-
工作流程
- 调用allowRequest()判断是否允许将请求提交到线程池
- 如果熔断器强制打开,circuitBreaker.forceOpen为true,不允许放行,返回。
- 如果熔断器强制关闭,circuitBreaker.forceClosed为true,允许放行。此外不必关注熔断器实际状态,也就是说熔断器仍然会维护统计数据和开关状态,只是不生效而已。
- 调用isOpen()判断熔断器开关是否打开
- 如果熔断器开关打开,进入第三步,否则继续;
- 如果一个周期内总的请求数小于circuitBreaker.requestVolumeThreshold的值,允许请求放行,否则继续;
- 如果一个周期内错误率小于circuitBreaker.errorThresholdPercentage的值,允许请求放行。否则,打开熔断器开关,进入第三步。
- 用allowSingleTest()判断是否允许单个请求通行,检查依赖服务是否恢复
- 如果熔断器打开,且距离熔断器打开的时间或上一次试探请求放行的时间超过circuitBreaker.sleepWindowInMilliseconds的值时,熔断器器进入半开状态,允许放行一个试探请求;否则,不允许放行。
- 调用allowRequest()判断是否允许将请求提交到线程池
-
-
服务降级
-
服务降级简介
-
触发条件
- 执行construct()或run()抛出异常
- 熔断器打开导致命令短路
- 命令的线程池和队列或信号量的容量超额,命令被拒绝
- 命令执行超时
-
使用方法
- 重写HystrixObservableCommand的resumeWithFallback方法或HystrixCommand的getFallBack方法
-
降级方式
- Fail Fast 快速失败
-
-
快速失败是最普通的命令执行方法,命令没有重写降级逻辑。 如果命令执行发生任何类型的故障,它将直接抛出异常。
-
-
-
-
-
- Fail Silent 无声失败
-
-
-
-
指在降级方法中通过返回null,空Map,空List或其他类似的响应来完成。
-
-
-
-
-
- Fallback: Static
-
-
-
-
指在降级方法中返回静态默认值。 这不会导致服务以“无声失败”的方式被删除,而是导致默认行为发生。如:应用根据命令执行返回true / false执行相应逻辑,但命令执行失败,则默认为true。
-
-
-
-
-
- Fallback: Stubbed
-
-
-
-
当命令返回一个包含多个字段的复合对象时,适合以Stubbed 的方式回退。
-
-
-
-
-
- Fallback: Cache via Network
-
-
-
-
有时,如果调用依赖服务失败,可以从缓存服务(如redis)中查询旧数据版本。由于又会发起远程调用,所以建议重新封装一个Command,使用不同的ThreadPoolKey,与主线程池进行隔离。
-
-
-
-
-
- Primary + Secondary with Fallback
- 有时系统具有两种行为:主要和次要 或 主要和故障转移。
- 主要和次要逻辑涉及到不同的网络调用和业务逻辑,所以需要将主次逻辑封装在不同的Command中,使用线程池进行隔离。为了实现主从逻辑切换,可以将主次command封装在外观HystrixCommand的run方法中,并结合配置中心设置的开关切换主从逻辑。
- 由于主次逻辑都是经过线程池隔离的HystrixCommand,因此外观HystrixCommand可以使用信号量隔离,而没有必要使用线程池隔离引入不必要的开销。
- Primary + Secondary with Fallback
-
-
-
-
-
Reference
- spring官方文档
https://spring.io/projects/spring-cloud-netflix
- 【狂神说Java】SpringCloud最新教程IDEA版
https://www.bilibili.com/video/BV1jJ411S7xr
- Hystrix原理与实战
https://blog.csdn.net/loushuiyifan/article/details/82702522
- Hystrix的原理及使用