如何设计高可用系统之故障隔离

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 简单来说,当功能或性能不符合预期,就是故障。减少故障的方式有多种,包括系统优化、监控、风险扫描、链路分析、变更管控、故障注入演练、故障隔离等。故障隔离是其中一种手段,并且要求在系统设计时就需要考虑清楚。

image.png

作者:大谷

什么是故障

简单来说,当功能或性能不符合预期,就是故障。

故障有两个比较重要的衡量指标:

RPO(Recovery Point Objective):主要指的是业务系统能容忍的最大数据丢失量,针对的是数据丢失。对于资金业务来说,一般 RPO 不能大于 0 的。

RTO(Recovery Time Objective): 主要指的是所能容忍的所业务停止服务的最长时间,针对的是服务丢失。

从单系统的角度看故障

image.png

一个系统,从头到脚,有非常多的故障点,所以,对于一个分布式系统来说,一定要假定故障是随时、而且一定会发生的。

故障隔离的目的

减少故障的方式有多种,包括系统优化、监控、风险扫描、链路分析、变更管控、故障注入演练、故障隔离等。故障隔离是其中一种手段,并且要求在系统设计时就需要考虑清楚。

从系统的角度看

故障隔离是指在系统设计的时候,要尽可能考虑故障的情况,当存在依赖关系的系统、系统内部组件或系统依赖的底层资源发生故障后,采取故障隔离措施可以将故障范围控制在局部,防止故障范围扩大,增加对上层系统可用性带来的影响。

并且当故障发生时,我们能够快速定位故障源,为后续的故障恢复提供必要条件。

从业务的角度看

故障隔离是为保障重点业务和保障重点客户,本质上弃卒保车的做法。

所以,各个业务域需要定义出哪一些是重点业务和客户。区分办法视各个业务而定。

有一种区分是按核心功能、重要功能、非关键功能三个等级来区分。比如,对于支付业务来说,支付肯定是一级业务,像营销、限额等是二级业务,而运营管理功能是三级业务。

故障隔离的基本原理

  • 故障时能够切断依赖
  • 服务或资源隔离,避免共享
  • 避免同步调用

故障隔离的常用模式

第一、依赖降级

默认降级

默认降级就是当依赖方出现问题的时候,采用默认的策略进行处理,而不是直接系统异常。

它的另一个变种是需要通过开关才能打开降级能力,一般用于一些比较谨慎的场景,比如对整个营销系统依赖进行降级。

例子 1

当依赖的缓存系统故障时,业务处理不是直接失败,而是捕捉到异常后,直接降级调用数据库

例子 2

在支付的场景中,会有每日提现额度的限制(是一个二级业务)。当依赖的限额系统出现故障时,针对小额提现的场景,可以默认降级,不依赖于限额系统。并且,同时,会记录下哪些使用额度没有同步到限额系统,在限额系统恢复后,将故障期间的使用额度同步到限额系统。

动态切换(Failover)
当故障发生时,动态地切换到备用方案

例子 1

数据库的主备切换机制,独立的 HA 心跳机制检查主节点状态,一旦主节点不可用,则切换到备节点。类似的,缓存系统的备节点也是类似原理。

当然,这种方式常常是异步复制,RPO>0

例子 2

如下图,是针对流水型数据 master 节点故障的 FO 方案,可以做到 RPO=0。什么是流水型数据,就是比较支付订单,贷款请求等这种一次请求生成一条的业务数据。

当 master 库故障时,因为是异步复制的,如果直接切到 slave 库,可能会导致数据丢失。那么 FO 的方案,就是直接切到一个空的 FO 库,对于新数据就读写到新的 FO 库。这个时候的影响,是老数据无法访问,但是新业务还能继续运行,比如支付业务还是继续跑。

这个时候再人工恢复 master,保证其与 slave 数据完成同步。再把调用连接切回到 master。

一般来说,已经写到 FO 库的数据可以回迁,也可以不回迁,应用通用流水号的 FO 标记则能识别应该调用哪一个库。

image.png

例子 3

如下图,也是针对于消息型数据 master 故障时的 markdown 方案。比如消息中心的存储就是用这种方案。

消息型跟流水型数据的区分是,消息型通用路由规则是随机的,放在哪都无所谓,而流水型如何使用分库分表方案,通用路由规则需要固定。比如基于 user_id 的后 2 位路由到库表,这个不能随意变更,不然会导致数据大量错乱,可能会导致大量的跨库事务问题和恢复后数据无法回查的问题。

这个方案类似于例子 2 的思路。不同点在于它是多个节点都是活跃的,每一次请求可以随机写到其中某一个库,当其中一个库挂了,那只会影响 50% 的老数据,对于另外 50% 的老数据和新增的数据是不会有影响的。而且这个方案还可以继续扩展,可以搞成 N 组数据库。

image.png

动态调整请求
当依赖方某些节点出现故障时,自动调整调用频率,或直接剔除

例子 1

消息中心在推送消息时,会根据消费节点的响应时间,调整推送的消息数量和间隔,避免将消费节点打挂。比如:推送时发生消费节点最近 5 秒平均响应时间超过 1000ms,则减少消息推送量,加大推送间隔。如果下次推送时间恢复正常,则逐步加大推送量,减小间隔,多次之后,如果消费节点没有问题,则恢复到正常的节奏。

例子 2

rpc 调用时,当调用节点出现调用异常时,有多种动态调节策略。比如节点不可用,直从消费节点中剔除。如果是节点响应明显变慢,则会降低路由的权重,减少调用次数。还有就是节点冷启动的情况,当节点第一次调用时,会灰度先调用少量数量,使系统先完成初始化,再逐步加大权重,直到跟其他节点权重一样。

快速失败(Fail Fast)
快速失败是指依赖方不可用时,不能耗费大量的宝贵系统资源(比如线程,比如连接数)等,在等待其恢复,而是要在其超出合理的功能和性能要求时,快速失败,避免占用资源,反而拖垮系统

例子 1

rpc 调用时,如果不设置超时时间,当调用方不可用时,同时会耗尽调用方的线程池,从而拖垮调用方。如果调用方的上游也是这样,那就会导致整体雪崩。

例子 2

数据库执行 SQL,如果不设置超时时间,当出现不合理的慢 SQL 时,很快会耗尽连接池,新的业务请求拿不到连接,就会一直挂起等待,很快你的线程池和请求队列会被占满,系统严重不可用。如果请求队列连是无界队列,则有可能直接 OOM。

缓存依赖数据
依赖数据本地缓存是一种数据的降级方案,当依赖方故障时,可以一定程度地保障业务可用。一般是应用于核心,决不能挂的业务场景。

例子 1

客户系统是一个全部系统都会依赖的非常核心的系统,如果其不可用,则会导致全局性的业务不可用,非常凶险。其中一种处理办法,就是客户信息的本地缓存,一方面可以降级被客户系统的调用压力,另一方面,当客户系统异常时,只要是已经缓存客户,还是可以继续使用业务,可以最大限制的保障业务可用。

这里的技术难点是在于缓存的一致性更新。通常有几种策略,一种是定时更新(客户系统正常时),另一种是本地缓存由客户系统提供一个统一的 sdk 管理,提供一种机制,当某一个用户变更时,通用各种 sdk 方更新数据。

另一个变种是,是将缓存放在 Tair,而不是调用方本地。这样,数据一致性的维护会更简单一些。目前内部用的最多是这种方案。

例子 2

归因系统会依赖于 Tair 中的配置信息,来判断请求中的参数(pub 值)是否合法。可以将配置信息(不大),缓存到调用方本地,这样,当 Tair 故障时,只会影响少量新增的配置,对于大部分业务功能是可用的。

减少或不要对低级别系统的依赖
这个是一种依赖原则,因为高级别系统的可用性标准(可用率、性能等)一般是高用于低级别系统的,如果依赖于低级别系统,当它发生故障时,高级别系统也会故障。这样本本质上是将高级将系统的可用性拉低到低级别系统的水平。

例子 1

比如,在支付系统去依赖于运营后台的话,假设原来支付系统的可用率是 99.99%,而运营后台的可用率是 99%,一旦这么依赖且无降级方案,则支付系统的可用率也会降低为 99%。

第二、功能降级

限流
限流的目的,是通过牺牲超过系统处理能力的部分业务,来保全系统整体的可用。

例子 1

所有 API 网关都会有限流的能力,常用的算法有计数器、漏桶算法,令牌桶算法等,总之就是会保护在一定时间内的并发数或者请求数。避免突发流量或者恶意的攻击,将系统打垮。

例子 2

归因系统中,收到归因请求,不会直接处理,而是先落到队列中(用数据库实现),然后按一定的时候频率,每次捞取一定量的数据处理,这样可以匀速地处理系统请求,削峰填谷,避免突发的请求数将系统打挂。

关闭非关键业务
就是关闭非关键的业务,不让其影响关键业务

例子 1

支付账单在双 11 高峰的时候,关闭实时更新账单的能力,改为双 11 过后,批量处理。

例子 2

双11高峰的时最为关键的业务,就是担保交易的付款,其它业务的重要性都会相对较低,比如收费业务,完全可以晚一点再向商户收取,以及淘宝的一些通过 CAE 代扣进来的返佣等业务,也可以一定程度上的滞后。

例子 3

消息中心提供了积压查询的功能,但是该功能是一个非关键功能,并且非常的耗费 DB 性能。大促时或系统处理能力紧张时,可以关闭掉,等恢复正常再打开。

第三、日志降级

顾名思义,就是将日志从 INFO,甚至 DEBUG 级别降为 WARN 或 ERROR 级别。

例子 1

写日志是比较消耗性能的操作,而且会占用大量磁盘,比如在大促时,或者因为日志过大导致磁盘打满时,可以通用日志降级来处理。

服务或资源隔离

概述一下,一般来说,隔离的级别可以有很多,如下图,不同的处理办法会不同在级别上进行。

image.png

用户级别隔离
将不同用户所使用的资源分开,保障部分资源故障时,只影响部分用户,而不是全体用户。

例子 1

支付宝按 RZONE 拆成逻辑机房,每个 RZONE 机房都会有完整的业务处理能力。用户请求的时候,会根据其 id 号,按配置的路由规则路由到某一个 RZONE 中。

这样,当部分 RZONE 异常时,只会影响跌幅到这部分机房的用户,而不是全局不可用。

例子 2

分库分表的时候,将用户相关的数据独立成一组分库。用户处理请求的时候,会根据其 id 号路由到其中一个分库上。这样,当用户数据相关的数据库的某一个实例宕机时,只会影响其中的部分用户,比如分了 10 个分库,则只会影响 10% 的用户,而不是全局用户。

业务功能隔离
将不同业务功能所使用的资源隔离,不互相影响

例子 1

贷款系统中,将还款和借款拆分不同系统。因为前者是一个非实时的批处理系统,而后者是对实时性和稳定性要求很高的联机系统,是用户可以直接感知到的。

拆分后,不会因为在批处理高峰期占用过多资源而影响借款系统的稳定性,而且两者在运维上,也可以更方便地根据各自的需要评估所需的机器数据。

例子 2

支付系统中,支付是关键业务,而账单是非关键业务,而且后者涉及到一些大数据量的查询(尤其是大商户),这些查询如果使用在线库查询,会影响支付的稳定性。所以,这两个业务进行了拆分。拆分后,支付业务会将数据异步同步给账单系统,账单系统会保存到更适用于账单查询的大数据存储中(HBase 或 ES)。

系统资源隔离
将不同的请求和所使用的资源隔离,不互相影响。

例子 1

API 请求网关,如果所有 API 请求都使用一个线程池,则当某一个 API 处理过慢的时候,会因为占满了线池,而导致其他 API 也不可用。所以,需要将不同的 API 分组,分配不同的线程池,避免互相影响。

例子 2

数据库集群管理的时候,将关键业务的实例和非关键业务的实例分开到不同集群,保障非关键的实例发生异常拖挂系统时,不会影响到关键业务的实例。

异步化处理

分阶段处理
分阶段就是主要逻辑就是避免同步调用,因为同步调用意味着强依赖,当依赖方故障时,调用方也会异常,而且这个异常可能会被用户感知。换句话讲,就是尽量异步化处理。

例子 1

消息的处理过程主要分为消息投递和消息接收两个阶段,如果两个处理阶段强耦合在一起,当某阶段出现故障,会影响另一阶段的处理。

例子 2

支付请求分至少分为支付受理、支付处理、支付结果回调三个阶段,如果这三个阶段分阶段异步处理,那么这其中某一个步骤异常了,都会导致支付失败,支付成功率低,用户体验差。而且,因为需要等待所有步骤都处理完,接口的性能也会很差。

例子 3

归因请求处理,分为接收处理(只有简单的归类逻辑)、归因处理(会有复杂的业务逻辑和外部依赖)两个阶段。同上,如果强耦合,当某阶段出现故障,会影响另一阶段的处理。

相关实践学习
【涂鸦即艺术】基于云应用开发平台CAP部署AI实时生图绘板
【涂鸦即艺术】基于云应用开发平台CAP部署AI实时生图绘板
目录
相关文章
|
云安全 人工智能 安全
重磅发布,阿里云安全大模型正式投入使用
2023年云栖大会,阿里云安全正式宣布基于通义千问大模型训练的安全大模型投入使用。首期开放的功能包括为用户提供定制化的安全告警解读、事件调查及处置建议服务,覆盖全网超过99%的告警事件类型。即日起,用户可在阿里云安全中心免费使用体验。
重磅发布,阿里云安全大模型正式投入使用
GitHub开源大厂缓存架构Redis优化的文档被警告,900页全是干货
掌握Redis对Java程序员来说很有必要了。实际上,很少有人真的掌握了Redis的全部技巧,有些甚至连面试题都很难应付。那么,如何全面系统地学习Redis呢?
|
数据采集 人工智能 机器人
RPA+BPM:企业流程自动化的最佳拍档
RPA可以和BPM实现优势互补。BPM通过对业务管理规则和逻辑的科学梳理并显性化体现,给RPA提供了大脑和神经网络。RPA的所有行为依赖清晰可被定义的逻辑规则。BPM给了RPA所依赖的逻辑规则,就像BPM为RPA提供了大脑和神经网络。
3760 0
|
7月前
|
传感器 安全 机器人
《特斯拉Optimus Gen - 2:多模态感知如何重塑具身智能未来》
特斯拉推出的Optimus Gen-2,凭借多模态感知技术成为机器人具身智能发展的里程碑。它通过视觉、听觉和触觉等多种传感器协同工作,实现对环境的全面理解。视觉摄像头帮助其精准导航与避障,高精度麦克风使其理解语音指令,触觉传感器让操作更加细腻安全。这些能力使Optimus Gen-2能快速适应工厂、家庭等复杂场景,提升人机协作效率,并在医疗、教育等领域展现潜力。多模态感知技术不仅推动了机器人自主学习与决策能力的发展,还预示着未来机器人将更深入地融入人类社会,为生产与生活带来革命性变化。
349 0
|
机器学习/深度学习 算法
贝叶斯线性回归:概率与预测建模的融合
本文探讨了贝叶斯方法在线性回归中的应用,从不确定性角度出发,介绍了如何通过概率来表达变量间关系的不确定性。文章首先回顾了古希腊天文学家使用本轮系统模拟行星运动的历史,并将其与傅里叶级数分解方法类比,强调了近似的重要性。接着,通过高斯分布和贝叶斯推断,详细讲解了线性回归中的不确定性处理方法。文章使用Howell1数据集,展示了如何构建和拟合高斯模型,并通过先验预测模拟验证模型合理性。最后,介绍了多项式回归和样条方法,展示了如何逐步增加模型复杂性以捕捉更细微的数据模式。贝叶斯方法不仅提供了点估计,还提供了完整的后验分布,使得模型更具解释性和鲁棒性。
371 1
贝叶斯线性回归:概率与预测建模的融合
|
存储 安全 网络安全
蜜罐技术:如何跟踪攻击者的活动
【10月更文挑战第22天】蜜罐是一种用于网络安全的系统,通过模拟漏洞吸引攻击者,记录其行为以研究攻击手法。分为高交互和低交互两种类型,前者提供真实操作系统服务,后者仅模拟部分系统功能。蜜罐有助于收集恶意软件样本,分析攻击者行为,提高网络安全防御。
335 3
|
监控 前端开发 JavaScript
记录浏览器节能机制导致Websocket断连问题
近期,在使用WebSocket(WS)连接时遇到了频繁断连的问题,这种情况在单个用户上每天发生数百次。尽管利用了socket.io的自动重连机制能够在断连后迅速恢复连接,但这并不保证每一次重连都能成功接收WS消息。因此,我们进行了一些的排查和测试工作。
768 1
记录浏览器节能机制导致Websocket断连问题
|
机器学习/深度学习 人工智能 边缘计算
|
消息中间件 测试技术 领域建模
DDD - 一文读懂DDD领域驱动设计
DDD - 一文读懂DDD领域驱动设计
43186 6
|
SQL 存储 Oracle
SQL优化2020最全干货总结---MySQL
BATJTMD等大厂的面试难度越来越高,但无论从大厂还是到小公司,一直未变的一个重点就是对SQL优化经验的考察。一提到数据库,先“说一说你对SQL优化的见解吧?”。
30865 2