什么是熔断?
熔断简单来说就是在单个服务出现问题,不可用时,为了避免引发更严重的问题,导致整个服务链路不可用的情况下,可以采用熔断的方式来避免。熔断一般情况下意味着服务的降级,可以理解为是一种异常兜底策略,需要服务的上游调用方来实现。
在访问量比较高的情况下,客户端访问A节点,A节点一个依赖的服务节点B出现延迟(或者不可用),这种情况下,无论是重试策略(重试3次)也好,或者超时策略(超过1S返回失败或者默认结果)也好,都会比正常请求消耗更多的资源,这时在流量高的场景有可能造成A服务资源被沾满,从而导致A服务其他接口也出现延迟或者不可用情况,再严重一些可能会出现雪崩,A服务依赖的上游也不可用,进而整个集群链路崩溃。
因此,服务异常时可以通过熔断的方式来进行快速的失败,避免后续流量继续请求到服务B,避免雪崩。
熔断更多的是指服务之间的熔断,熔断通常都会有恢复策略。
什么是降级?
降级,其实也是一种兜底策略,可以理解为主方案行不通了,换一个备用方案。比如,查询缓存失败,改为查询数据库,查询数据库也失败,返回“系统繁忙”给到用户,这种就是降级。再比如,A服务调用B服务,调用失败,返回“系统繁忙”也算降级,如果此时进行重试,那么可以叫“容错”。
降级其实有很多方案:
- 被动降级:服务不可用时,返回备用数据,或者提示文案。比如,广告推送,正常推送查询用户感兴趣的,如果服务不可用,改为推送默认广告。
- 主动降级:人为的把服务设置为不可用。比如双十一,将评论,收藏等功能主动降级为不可用,避免用户请求占用服务资源。一般通过预制的降级开关实现。收到客户端请求后,不会真的去请求评论,收藏服务,直接返回用户,功能不可用。
- 自动降级:通过预制的规则,自动的实现降级,自动实现恢复。比如,熔断导致的降级,限流触发降级,超时降级等。
自动降级与被动降级区别主要是:是否可以自动恢复。
被动降级强调的是,不受控制。可以理解为简单的if、else每次请求过来都会进行判断。
自动降级强调的是,自动化。可以理解服务出现问题,达到阈值条件时拒绝后续请求,同时感知服务状态,达到可用条件时,再允许后续请求。
总结
其实无论是熔断也好,降级也好。主要都是因为,由于服务之间存在依赖性,故障会传播造成连锁反应,导致雪崩。为了避免雪崩从而提出了熔断和降级的概念。
区别
熔断更注重的是服务之间请求的问题,强调服务之间的请求可以实现自我恢复。面对的是服务异常问题。
降级更注重功能的主次问题,在资源有限的情况下优先保证核心功能可用,次要功能做降级处理。面对的是服务资源。
可以理解为,熔断是降级的一种,属于自动降级。(降级也是限流的一种方式)
Hystrix
简介
Hystrix简单来说就是:对某个服务的调用在一定的时间内(默认10s,由 metrics.rollingStats.timeInMilliseconds配置),有超过一定次数(默认20次,由 circuitBreaker.requestVolumeThreshold参数配置)并且失败率超过一定值(默认 50%,由circuitBreaker.errorThresholdPercentage配置),该服务的断路器会打开。 返回一个由开发者设定的fallback。
作用
主要是通过快速失败,快速恢复,对服务依赖节点不可用故障进行保护,避免级联故障。能够快速的回退,实现服务降级。并且支持实时监控、告警和操作控制。
设计原则
- 使用快速失败代替请求排队,减少负载。
- 防止任何单个依赖故障耗尽容器所有线程资源。使用隔离的方式,减少故障带来的影响。
- 根据实际情况提供降级方法,保护用户不受影响。
- 通过实时的监控增强故障的感知。
Hystrix 如何实现熔断
场景
服务调用者A,需要调用服务提供者B的接口。考虑到B服务如果出现超时不可用情况,要减轻对A服务的影响。比如:B服务接口响应时间超过1S,开启降级,提供 fall back方法(服务调用者自行根据业务创建该方法),返回默认信息(也可以默认失败),在B服务接口出现不稳定(10秒内调用20次失败率50%以上时,开启熔断,后续5秒的请求直接走降级fall back 方法 返回默认信息)
熔断的默认触发机制
使用Histrix 默认策略 10秒内 调用20次 失败率50%以上 触发熔断 默认熔断 5s
步骤
服务提供者
- 提供服务接口。
- 模拟超时失败。
- 启动服务
服务调用者
- 引入jar包。
- 开启熔断配置注解。
- 启动配Enable注解。@EnableCircuitBreaker
- 如果使用Fein 对Fein 需要开启Histrix 支持(yml 配置文件增加 feign.hystrix.enabled=true)
- 在调用服务提供者接口处进行配置。使用注解方式。
- @HystrixCommand
- 也可以使用默认的配置属性
- 创建与注解配置一致的fall back方法
- fall back 是作为一个降级的方法,我们也可以不使用降级。
思考
如何定义失败
针对上述场景,模拟超时即为失败。那么对于线上的正常运行情况,如何定义失败?比如抛出了异常(正常响应时间内),又比如服务链接找不到。
回想一下熔断的目的,主要是为了保证服务提供方A不可用时,调用者B仍然耗费B自身的资源去请求不可用的服务A,此时B调用A耗费的资源如果比正常调用要多那么需要考虑熔断,避免B自身资源消耗完毕,引发雪崩。如果B调用A耗费的资源较少,那么熔断还有必要么?但是不做限制 似乎又有点傻,明知道不行还非要去尝试。而且如果出现异常情况,通常都是需要去记录堆栈,发送告警。
所以最终认为,失败可大概分为三种,超时、异常、服务不可用,都有必要做熔断。
如何确定生效的范围,有些接口配置,有些不需要配置。不同接口可以单独使用配置,也可以默认配置
对于服务A来说可能会调用N个不同的服务,那么对于不同的服务类型,可能会采取不同的降级措施,比如A调用B的情况比较少,那么可以将请求的次数适当调低一些,避免因设置过高,不满足熔断条件导致熔断不生效。
此外对于不同的服务提供者,还可以基于不同的情况,做服务隔离。常用的隔离方式有两种,1种是线程池的隔离,一种是信号量的隔离。其根本就是对不同的服务做不同的资源限制。避免一个服务不可用导致其他服务资源也被消耗完发生雪崩情况。
假如有三个服务A/B/C
线程池隔离是指,对服务A/B/C分配不同的线程池,A服务出现异常导致线程沾满,不会影响B/C服务的线程池,B/C服务扔可以正常的工作。
信号量隔离是指,使用同一个线程池,但是对不同的服务设置不同的信号量。比如线程池共有10个线程,A/B/C服务的信号量分别为2、3、5。每次请求都需要验证其对应的信号量是否有剩余。
Histrix 开启熔断后,如果没有对依赖的服务开启fall back 那么会如何?
如果没有配置fall back 会使用默认的方式。
Histrix 开启后 是针对所有的服务 还是针对特定的服务开启
开启时针对所有的服务。可以使用默认配置,
Histrix 优缺点,是否一定要开启?
个人认为,在分布式微服务环境中,一些流量较高的核心业务系统需要开启熔断,因为一个系统对外提供的肯定不止一种服务,如果不开启熔断,很容易导致某一种服务因为链路的某个依赖环节出现故障导致整个系统服务资源受影响。
如果服务流量较小,且非核心也并不是很重要,那么可以采用一种通用的配置,或者不用也可。因为配置也会有额外的成本,需要考虑合适的参数,服务隔离,fall back 而且很有可能配置了也没有什么太大的效果。
比如服务 QPS 10 以下, 开启 熔断 5秒其实并没有多大的价值。
Hystrix 原理
Hystrix主要是通过拦截器的方式,嵌入Feign的请求过程中。
- 在获取到请求后,会先验证是否已开启熔断开关,如果没有则放行,有则直接返回降级。
- 如果没有开启熔断开发,判断10s内是否已大于设置的阈值上线(20次),没有直接放行,有则需要获取失败占比
- 如果失败占比达到阈值(50%),直接开启熔断开关。同时记录开关开启时间,方便后续自动恢复。如果低于阈值,放行。