架构高并发--降级与熔断
引入
服务依赖之间导致错误
当用户请求 A、P、H、I 四个服务获取数据时,在正常流量下系统稳定运行,如果某天系统进来大量流量,其中服务 I 出现 CPU、内存占用过高等问题,结果导致服务 I 出现延迟、响应过慢,随着请求的持续增加,服务 I 承受不住压力导致内部错误或资源耗尽,一直不响应,此时更糟糕的是其他服务对 I 有依赖,那么这些依赖 I 的服务一直等待 I 的响应,也会出现请求堆积、资源占用,慢慢扩散到所有微服务,引发雪崩效应。
基本的容错模式
主动超时
:Http请求主动设置一个超时时间,超时就直接返回,不会造成服务堆积
限流
:限制最大并发数
熔断
:当错误数超过阈值时快速失败,不调用后端服务,同时隔一定时间放几个请求去重试后端服务是否能正常调用,如果成功则关闭熔断状态,失败则继续快速失败,直接返回。(此处有个重试,重试就是弹性恢复的能力)
隔离
:把每个依赖或调用的服务都隔离开来,防止级联失败引起整体服务不可用
降级
:服务失败或异常后,返回指定的默认信息
服务降级
由于爆炸性的流量冲击,对一些服务进行有策略的放弃,以此缓解系统压力,保证目前主要业务的正常运行。它主要是针对非正常情况下的应急服务措施:当此时一些业务服务无法执行时,给出一个统一的返回结果。
降级的服务特征
- 原因:整体负荷超出整体负载承受能力。
- 目的:保证重要或基本服务正常运行,非重要服务延迟使用或暂停使用
- 大小:降低服务粒度,要考虑整体模块粒度的大小,将粒度控制在合适的范围内
- 可控性:在服务粒度大小的基础上增加服务的可控性,后台服务开关的功能是一项必要配置(单机可配置文件,其他可领用数据库和缓存),可分为手动控制和自动控制。
- 次序:一般从外围延伸服务开始降级,需要有一定的配置项,重要性低的优先降级,比如可以分组设置等级1-10,当服务需要降级到某一个级别时,进行相关配置
降级方式
- 延迟服务:比如发表了评论,重要服务,比如在文章中显示正常,但是延迟给用户增加积分,只是放到一个缓存中,等服务平稳之后再执行。
- 在粒度范围内关闭服务(片段降级或服务功能降级):比如关闭相关文章的推荐,直接关闭推荐区
- 页面异步请求降级:比如商品详情页上有推荐信息/配送至等异步加载的请求,如果这些信息响应慢或者后端服务有问题,可以进行降级;
- 页面跳转(页面降级):比如可以有相关文章推荐,但是更多的页面则直接跳转到某一个地址
- 写降级:比如秒杀抢购,我们可以只进行Cache的更新,然后异步同步扣减库存到DB,保证最终一致性即可,此时可以将DB降级为Cache。
- 读降级:比如多级缓存模式,如果后端服务有问题,可以降级为只读缓存,这种方式适用于对读一致性要求不高的场景。
服务降级分类
- 降级按照是否自动化可分为:自动开关降级(超时、失败次数、故障、限流)和人工开关降级(秒杀、电商大促等)。
- 降级按照功能可分为:读服务降级、写服务降级。
- 降级按照处于的系统层次可分为:多级降级。
服务熔断
- 服务雪崩
多个微服务之间调用的时候,假设微服务A调用微服务B和微服务C,微服务B和微服务C有调用其他的微服务,如果整个链路上某个微服务的调用响应式过长或者不可用,对微服务A的调用就会占用越来越多的系统资源,进而引起系统雪崩,所谓的”雪崩效应” - 断路器
“断路器”本身是一种开关装置,当某个服务单元发生故障监控(类似熔断保险丝),向调用方法返回一个符合预期的、可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方法无法处理的异常,这样就保证了服务调用方的线程不会被长时间、不必要地占用,从而避免了故障在分布式系统中的蔓延。乃至雪崩。 - 服务熔断
熔断机制是应对雪崩效应的一种微服务链路保护机制,当整个链路的某个微服务不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回”错误”的响应信息。 - Hystrix
Hystrix是一个用于分布式系统的延迟和容错的开源库。在分布式系统里,许多依赖不可避免的调用失败,比如超时、异常等,Hystrix能够保证在一个依赖出问题的情况下,不会导致整个服务失败,避免级联故障,以提高分布式系统的弹性。
熔断流程
基本的断路器模式
基本的断路器有两种状态:
- close状态下,client向supplier发送的服务请求,无障碍通过断路器,supplier返回给client
- open状态下,断路器将不会进行转发
- trip:在close状态下,如果supplier持续报错,达到阈值的时候,就会从close到open状态
扩展的断路器模式
基本的断路器模式下,保证了断路器在open状态下,保护supplier不会被调用,我们可以使用额外的措施使得supplier在恢复服务后,可以重置断路器,定期进行supplier进行探测,此时断路器状态为half-open
服务熔断与服务降级
服务熔断对服务提供了proxy,防止服务不可能时,出现串联故障(cascading failure),导致雪崩效应。
服务熔断一般是由于下游服务引起,而服务降级一般是从整体负荷进行考虑
- 共性:
- 目的: 都是从可用性,可靠性,提高系统的容错能力。
- 最终表现: 使其一些应用不可达或者不可用,来保证整体的系统稳定。
- 粒度: 一般都是服务级别,但也有细粒度的层面。如做到数据持久层,只许查询不许增删改查
- 自治: 对其自治性要求很高,都要求具有较高的的自动处理机制。
- 区别:
- 触发原因: 服务熔断一般是由于下游服务不可用引起,而服务降级一般为整体系统考虑。
- 管理目标: 熔断是每个微服务都需要的,是一个框架级别的处理;而服务降级一般是关注业务,对业务进行考虑,抓住业务的层级,从而决定在哪一层上进行处理。
- 实现方式: 代码实现中的差异
服务熔断中需要考虑的设计
源自博主张善友的观点:
- 异常处理
- 异常的类型
- 日志
- 手动重置
- 加快熔断器的熔断操作
方案: Hystrix
Spring Cloud Netflix Hystrix就是隔离措施的一种实现,可以设置在某种超时或者失败情形下断开依赖调用或者返回指定逻辑,从而提高分布式系统的稳定性.
Hystrix设计原则:
- 防止单个服务的故障,耗尽整个系统服务的容器的线程资源,避免分布式环境里大量级联失败。通过第三方客户端访问(通常是通过网络)依赖服务出现失败、拒绝、超时或短路时执行回退逻辑
- 用快速失败代替排队(每个依赖服务维护一个小的线程池或信号量,当线程池满或信号量满,会立即拒绝服务而不会排队等待)和优雅的服务降级;当依赖服务失效后又恢复正常,快速恢复
- 提供接近实时的监控和警报,从而能够快速发现故障和修复。监控信息包括请求成功,失败(客户端抛出的异常),超时和线程拒绝。如果访问依赖服务的错误百分比超过阈值,断路器会跳闸,此时服务会在一段时间内停止对特定服务的所有请求
- 将所有请求外部系统(或请求依赖服务)封装到HystrixCommand或HystrixObservableCommand对象中,然后这些请求在一个独立的线程中执行。使用隔离技术来限制任何一个依赖的失败对系统的影响。每个依赖服务维护一个小的线程池(或信号量),当线程池满或信号量满,会立即拒绝服务而不会排队等待
Hystrix特性
- 请求熔断:当Hystrix Command请求后端服务失败数量超过一定比例(默认50%), 断路器会切换到开路状态(Open). 这时所有请求会直接失败而不会发送到后端服务. 断路器保持在开路状态一段时间后(默认5秒), 自动切换到半开路状态(HALF-OPEN).
这时会判断下一次请求的返回情况, 如果请求成功, 断路器切回闭路状态(CLOSED), 否则重新切换到开路状态(OPEN). Hystrix的断路器就像我们家庭电路中的保险丝, 一旦后端服务不可用, 断路器会直接切断请求链, 避免发送大量无效请求影响系统吞吐量, 并且断路器有自我检测并恢复的能力. - 服务降级: fallback相当于是降级操作, 对于查询操作,我们可以实现一个fallback方法,当请求后端服务出现异常的时候,可以使用fallback方法返回的值,fallback方法返回的默认值或者来自缓存,告知后面的服务不可用。
- 请求缓存:比如一个请求过来请求我userId=1的数据,你后面的请求也过来请求同样的数据,这时我不会继续走原来的那条请求链路了,而是把第一次请求缓存过了,把第一次的请求结果返回给后面的请求。
- 请求合并:我依赖于某一个服务,我要调用N次,比如说查数据库的时候,我发了N条请求发了N条SQL然后拿到一堆结果,这时候我们可以把多个请求合并成一个请求,发送一个查询多条数据的SQL的请求,这样我们只需查询一次数据库,提升了效率。
hystrix流程
1. 调用创建与封装
- 每次调用创建一个新的
HystrixCommand
,并将依赖调用封装在run()
方法中。
2. 执行调用
- 通过
execute()
进行同步调用,或queue()
进行异步调用。
3. 熔断器状态判断
- 判断熔断器 (
circuit-breaker
) 是否打开:- 打开:跳到步骤 8 进行降级策略。
- 关闭:进入步骤 5。
4. 资源状态检查
- 判断线程池/队列/信号量是否跑满:
- 跑满:进入 降级步骤 8。
- 未跑满:继续 步骤 6。
5. 依赖逻辑执行
- 调用
HystrixCommand
的run()
方法运行依赖逻辑:- 6a:依赖逻辑调用超时,进入 步骤 8。
6. 逻辑调用结果判断
- 判断逻辑是否调用成功:
- 7a:返回成功调用结果。
- 7b:调用出错,进入 步骤 8。
7. 熔断器状态计算
- 统计所有的运行状态(成功、失败、拒绝、超时)并上报给熔断器,以判断熔断器状态。
8. 降级逻辑 getFallback()
以下四种情况将触发
getFallback()
调用:run()
方法抛出 非 HystrixBadRequestException 异常。run()
方法调用超时。- 熔断器开启拦截调用。
- 线程池/队列/信号量是否跑满。
降级逻辑处理:
- 9a:如果
Command
**未实现getFallback()
**,则直接抛出异常。 - 9b:
fallback
逻辑调用成功,直接返回。 - 9c:降级逻辑调用失败,抛出异常。
- 9a:如果
9. 返回执行结果
- 返回成功的执行结果。
Hystrix 与 Ribbon 集成
这里接着前面的 Ribbon 进行 Hystrix 集成。
- 想要对一个请求进行熔断,必须 拦截 该请求,并包装一层进行控制,比如执行熔断逻辑。因此,需要在 Ribbon 上进行集成。
- Ribbon 是请求发起的地方:
- 负载均衡时,Ribbon 拦截请求。
- 进行熔断时,Ribbon 再次拦截请求,并执行熔断策略。