容灾是什么
前言
从字面上理解,容灾是指在自然灾难,设备故障,人为破环等灾难发生时,通过一些前置的兼容或包容性处理,保证整个持续有效的运行。
容灾是一个比较宽泛的概念,是一个系统性的工程,所以在我们谈及容灾的时候,往往需要先建立出面临灾难的场景,针对不同场景来实施不同容灾方案。
关键指标
RTO 与 RPO 都是用来指导制定容灾方案,和衡量方案优劣的关键指标。
RTO:Recovery Time Objective,指从灾难发生到整个系统恢复正常所需要的最大时常。
例如 RTO 指标为 30 min,则代表当前业务系统能够容忍 30 min 时间的停止服务,在停止服务的期间对业务造成的损失是可承受的。但如果 30 min 后仍无法恢复服务,会导致业务损失不可承受。
RPO:Recovery Point Objective,指业务系统可容忍的数据丢失量,从时间维度上计算的数据丢失量。
例如 RPO 指标为 10 小时,业务系统数据会在每天 0 点进行数据备份,若当天早上 8 点系统故障导致数据全丢失,则丢失的数据量就是 8 小时的,小于 RPO 指标,是在可容忍范围的。若当天下午 14 点系统故障数据丢失,则是不可容忍的。
常见故障
一般系统故障会分为三个大类型:状态型,区域型和业务型。根据不同的故障类型需要采取不同的容灾方案,甚至是多种容灾方案组合使用。
容灾除了在发生故障后及时修复,如何在故障发生前制定合适的故障预防方案来避免故障的发生也是尤为重要。
云时代还需要容灾吗
随着云原生时代的到来,企业纷纷倡导将业务容器化拥抱云原生,云原生也在这种趋势下发展出了适合云原生的容灾方案。
当前云原生容灾基本以数据备份,负载均衡和递进式多活容灾为核心,以数据迁移,备份和恢复为业务场景,实现高可靠性。以下未高亮的就是云原生容灾目前能够覆盖的范围。
可以看到,如今云原生容灾几乎能够覆盖绝大多数通用场景,但与业务类型强耦合的容灾方案还是需要工程师来制定实现。
容灾方案
从上面的思维导图中可以发现,针对不同的故障有不同的容灾方案,没有一劳永逸的方案,合适的方案才是好方案。
中心化判定
中心化判定通常是在客户端与服务端之间建立中心代理层,或者称网关层,有能力进行统一的流量管理,负载均衡等一系列容灾操作。
中心化判定的优势:
- 切换快速,且无感知
- 与业务分离,专注于容灾
- 负载均衡控制更精确
同时中心化也存在明显的劣势:
- 存在单点故障风险,若中心代理服务出现问题,整个业务系统会受到影响
- 维护成本较高
- 自身容灾可能脑裂
- 异构适应性弱
分布式判定
分布式判定法通常是在每个客户端建立本地的代理层,每次客户端发出请求都会经过代理层,代理层根据当前策略转发至对应的服务器。
分布式模式解决了中心化两个比较重要的的问题,一是单点故障风险,因为每个客户端都有属于自己的代理层,即使一个客户端的代理层故障也不会影响到其他客户端;二是异构适应性强,因为每个代理层只服务对应客户端,所以代理层可以根据不同客户端做出定制化的配置。
但分布式模式存在一个比较大的缺点,就是代理层与客户端耦合性比较强,导致代理层的转发策略具有单点局限性,并且这个局限性会随着时间的推移越来越大,不能做到有效的负载均衡。所以我们需要引入一个策略管理中心的模块,正常情况下,客户端的每次请求经过本地的代理层,上报至策略管理中心,策略管理中心一面收集各客户端的请求,根据现状给出对应的转发策略下发至对应代理层,代理层再根据管理中心下发策略进行请求转发;若策略管理中心模块出现故障,则启用本地代理层策略进行转发。
分布式判定的优势:
- 没有单点故障风险
- 运维成本比较低
- 异构适应性强
同时中心化也存在明显的劣势:
- 切换速度慢,且可能存在流量切换不干净的情况
- 对应用程序入侵较多,耦合性比较强
- 长链接负载均衡不理想
单元化设计
单元是指一个能完成所有业务的集合。
这里的单元化其实区别于模块化的概念,模块化通常是指将某个功能抽象成模块,不同的模块组合,协同工作来支撑所有业务。而单元化是指要包含所有业务模块的单元,意味着每个单元都需要有独立运行为所有业务提供服务的能力。
这里的所有业务并不一定是全部的业务,一般来说是指所有的核心业务。单元化容灾的初衷是保障核心业务不会受到或受到可接受的影响,所以在做单元化容灾时一定要做好取舍,要考虑清楚哪些业务是需要单元化。
常见的单元化设计方案有:同城双活,异地双活,多城多活等。
以同城双活为例,在同一个城市搭建两个机房,提供两套完整的服务和数据库,每套数据库通常是一主一备,以下是个简化模型。
两台 IDC 服务 A 和 服务 B 为保障强一致性,都访问 DB1。当 DB1 出现故障,访问数据库立即切换至 DB3,一般能保证秒级的 RTO。
其中 DB 之间的数据同步策略可以根据实际场景中 RPO 的要求来制定。例如:若在对 RPO 要求比较严格的场景中,可以使用 DB1 向 DB3 和 DB4 半同步的方式进行数据同步,牺牲部分性能来换取强一致性。
单元化设计的前提是需要业务数据具备可水平切分性,比如按照用户 ID 的规则进行切分等。这也看出来单元化设计不仅仅是针对于容灾,也可以解决随着业务扩张数据库连接数过多等一系列扩容问题。
过载保护
过载保护通常会采用三种思路保护系统:限流,降级和熔断。
限流
限流一般是指限制入口流量。在流量过大时,触发预设限流开关,采取相应的限流行为,保护下游服务。通常触发开关的机制有三种,分别是:设定阈值,下游通知和资源过载定时检测。保护下游的限流行为也有两种,一种是刚性的,指流量超过预设阈值后,关闭与下游服务的通讯,不再提供服务;一种是弹性的,一般指降级处理。
降级
降级一般是指限制业务能力或是限制用户行为。限制业务能力一般会保留主要功能,限制次要功能,使整个系统处于一个可承受压力且主要可用的状态,来尽可能的保证用户体验。限制用户行为一般是指通过策略限制部分用户无法访问部分业务。
熔断
熔断一般指发现自身或下游无法提供正常服务,上游主动关闭与下游之间的通讯。目前也有不少弹性熔断的机制,如:Google SRE 弹性熔断等。
过载保护的具体措施有:队列限流,耗时限流,频率限流,随机限流,终端限流,轻重分离,动态过载等。
举个频率限流的例子
其中 “当前 QPS 超过平均值的判断和根据当前 QPS 计算请求丢弃概率” 这部分就是自定义的限流策略,这个策略可以是超过平均值也可以是达到理论峰值的百分之八十等,需要根据实际场景来制定。
总的来说,要做好过载保护,上下游的服务要做到互相信任,但不能互相依赖。
柔性处理
柔性处理通常指在有限的条件下无法提供完美的用户体验时,需要对业务系统进行取舍,为用户提供有损的完整服务。
因此实现柔性处理的前提是需要清楚的认识到哪些服务提供系统的核心功能,所以在产品设计阶段就应该结合当前资源和场景开始制定柔性处理方案。通常设计柔性处理方案的方法如下:
通常柔性处理的实际方案有以下几种方式:
- 功能消减
- 本地化
- 渠道备用
- 异步化
柔性处理是容灾中最为常用,也是成本较低的一种手段,例如网络差看视频时,会将视频画质自动降级;网络差打开网站时,有些网站会提供纯文字内容,图片和视频等部分不显示等。
如果对以上哪种方案具体实现有兴趣的可以评论区留言,交流学习一下。
尾言
了解容灾设计后,在日常业务开发的过程中,有部分开发者时常会有疑惑,容灾应该是大型系统级的方案应该考虑的问题,我开发的业务模块需要做容灾吗?如果需要做,在有限的开发周期内容灾应该做到什么程度呢?
首先容灾设计不是针对一个固定场景的,大型系统级的容灾可能需要考虑到多城多活等因素,而小的业务模块可能需要考虑的则是柔性方案等。真正好的容灾设计是针对不同的业务场景采用不同的容灾方案组合以达到真实容灾的目的。
而在有限的开发周期判断一个模块的容灾应该做到何种程度,取决于模块的价值,如果这个模块是关键模块,毫无疑问,对于关键模块需要制定完善的容灾方案。而如果模块价值不高,则可以进行一些简单的容灾。
学会权衡取舍,有限资源价值最大化是迈向架构师的重要一步。