微软的FMA(Failure Mode Analysis)设计方案, 将自动化的故障治理分为:
- Detection: 故障探测,从直接指标上发现是否有某种类型的故障
- Recovery: 故障恢复, 自动治理的过程,用于几时止损
- Diagnostics: 故障定位, 一般由扔接入,排查故障根因,从本质上解决问题
以过载为例:
Detection对应排队时间超过10ms
Recovery对应自适应限流
Diagnostics对应根因定位,判断是上游有突增流量还是服务自身变更引入bug导致
FMA的梳理
Failure | Detection | Recovery | Diagnostics | |
---|---|---|---|---|
APP崩溃 | 服务进程不可用 | 自动挂起 | ||
用户非法调用接口,或者是用非法调用使系统过载 | 每次请求校验身份信息,并且将请求与用户ID绑定 | - 对特定用户县里 - 封禁用户 |
||
服务更新引入bug | 节点健康实时检查 | 回滚到是一个可用版本 | ||
后端节点建立连接失败 | - 网络故障 - 后端服务未部署 - 授权失败 |
|||
写Azure Search失败 | 在调用端捕获异常 | 重试 | ||
读Azure Search失败 | 在调用端捕获异常 | 重试 | ||
Curmos DB读写失败 | 在调用端捕获异常 | - 重试 - 尽可能多副本部署DB |
||
消息队列写入失败 | - 本地缓存数据,消息队列可用后再写入 - 创建备用队列并且写入 |
|||
消费者无法处理的某个特定信息 | 将消息发送到死信队列 | |||
读Redis失败 | 在调用端捕获异常 | - 重试 - 缓存不命中,回源到DB |
||
写Redis失败 | 在调用端捕获异常 | - 重试 - 重试多次失败,暂时熔断,稍微写入缓存 |
||
SQL数据库建立连接失败 | 监控连接失败异常 | 前提是DB在多地区部署 - 读请求,自动化切换到副本 - 写请求,需要人工切换到副本 |
||
SQL客户端使用完所有连接池的连接 | 监控失败连接异常 | - 重试 - 客户端做连接池隔离 - 增加连接池上限 |
||
数据库连接池达到上限 | - 调用端捕获异常 - 监控特定的错误码 |
一般是瞬时的故障,即可以通过重试解决。如果持续命中这样的错误,意味着DB需要扩容 | ||
Service Bus(消息队列管理)读失败 | SDK侧捕捉异常,并且标识异常是否为瞬时的 | - 重试 - 重试几次还是失败后,将消息写入死信队列 |
||
Service Bus(消息队列管理)写失败 | SDK侧捕捉异常,并且标识异常是否为瞬时的 | - 重试 - 如果写失败为:队列quota用完,则需要排空队列的部分消息。调用端熔断 - 尽可能地使用分区队列或者Topic - 消费双发 |
||
Service Bus处理重复消息 | 检测ID和发送的数量 | - 尽量将消费的流程做到幂等,否则需要在消息消费前识别消息ID,判断是否消费过 - Service Bus检查消息ID,在一定事件窗口内,只会将不重复ID的消息放入消息队列 |
通过日志检查 | |
Service Bus消费侧对于特定消息处理失败 | 业务强相关场景,比如消息出现无效字段 | - 直接将消息移动到死信队列,之后执行独立的逻辑来检测死信队列 - [Peek Lock模式] 用于消费者内部逻辑异常的情况(例如panic等),这是给消息加锁,锁过期后,再次处理消息,如果达到最大加锁次数,或者超过一定时间,将消息移入死信队列 |
有消息进入死信队列,发送一个事件 | |
服务调用失败 | 下游服务返回error | 1.重试 2.有状态的服务做逻辑上的回滚 3.无状态服务保证处理的幂等性 4.错误率很高的需要熔断 |
应用日志 |
从以上表格看到,最通用的手段是重试,其次是熔断。
瞬时故障与非瞬时故障
- 首先基于下游错误码来判断是瞬时还是非瞬时
- 瞬时故障可以有限通过重试机制解决
- 重试超过次数没有成功,则将故障判定为非瞬时
- 非瞬时故障的处理与瞬时逻辑不同,一般有:熔断,切流,移入死信队列等
DB容灾
对DB容灾有特有的主备切换能力,其中读接口实现了自动切换,但是写接口需要人工手动切换。
写切主,需要考虑主备同步的问题。一般操作流程是,关闭写接口,等待写接口流量掉0,且主备数据完全同步之后,在切主,最后开放写接口
MQ容灾
缓存后写入
MQ写入失败后,Azure的做法是将数据缓存在本地,先给客户端返回成功,再异步地讲缓存数据写入MQ直到成功(如果一直没有成功会放入死信队列)。
这种做法将数据缓存在本地,会有实例重启/迁移导致数据丢失的风险,更保险的做法是将数据缓存在Redis。
死信队列
如果一个消息一直无法被消费,需要有个停止的方式,一般放在死信队列。
主备切换
为了应对写MQ故障,Azure
- 被动双发。写入主消息队列的错误率超过阈值后,切换写入到备消息队列
- 主动双发。同时将消息写入主备消息队列
- 消费侧做法。上述两种方式,消费者都要同时监听2个队列,并且需要针对消息做去重。