架构学习-高可用架构模式

1. CAP 原理

CAP原理讨论得是对于数据的读写操作,并不是在讨论整个系统的各个方面的功能。并且讨论得是在互联和分享数据过程当中出现的问题

  • Consistency 一致性
    • where all nodes see the same data at the same time
    • a read is guaranteed to return the most recent write for a given client
  • Availability 可用性
    • guarantee that every request receives a response about whether it succeed or fail
    • a non-faling node will return a reasonable response within a reasonable amount of time (no error or timeout)
  • Partition tolerance 分区容错性
    • the system continues to operate even if any one part of the system is lost or fails
    • the system will continue to function when network partitions occur

1.1. CAP的选择

分布式系统理论上来说无法选择CA架构,因为P是客观存在的,当网络分区发生的时候,为了保证C,那么只能回报错误给Client,这实质上就违反了A了。

1.1.1 CP架构

如下图所示,为了保证一致性,当发生分区现象后,N1 节点上的数据已经更新到 y,但由于 N1 和 N2 之间的复制通道中断,数据 y 无法同步到 N2,N2 节点上的数据还是 x。这时客户端 C 访问 N2 时,N2 需要返回 Error,提示客户端 C“系统现在发生了错误”,这种处理方式违背了可用性(Availability)的要求,因此 CAP 三者只能满足 CP。

fig1.png

1.1.2 AP架构

为了保证可用性,当发生分区现象后,N1 节点上的数据已经更新到 y,但由于 N1 和 N2 之间的复制通道中断,数据 y 无法同步到 N2,N2 节点上的数据还是 x。这时客户端 C 访问 N2 时,N2 将当前自己拥有的数据 x 返回给客户端 C 了,而实际上当前最新的数据已经是 y 了,这就不满足一致性(Consistency)的要求了,因此 CAP 三者只能满足 AP。注意:这里 N2 节点返回 x,虽然不是一个“正确”的结果,但是一个“合理”的结果,因为 x 是旧的数据,并不是一个错乱的值,只是不是最新的数据而已。

1.2 CAP 细节

1.2.1 关注粒度 - 数据

每个系统都不可能只处理一种数据,而是包含多种类型的数据,有的数据必须选择CP,有的数据必须选择AP。如果我们从整个系统的角度上去选择CP还是AP,很容易顾此失彼,无论怎么做都有问题。

比如用户管理系统,那么用户账户数据会选择CP,而用户信息数据会选择AP。因为在真的对CAP做应用的时候,我们需要将系统内的数据按照不同的应用场景和要求来进行分类,每类数据选择不同的策略,而不是限定整个系统的所有数据都是同一个策略。

1.2.2 CAP是忽略网络延迟的

从节点A到节点B的复制,一定是需要花费一定的时间的,从几毫秒到几十毫秒不等。这意味着,CAP的理论当中的C在实践中是不可能完美实现的。

因此对于和金钱相关的,或者是和抢购相关的商品库存,技术上是无法做到分布式场景下完美的一致性的。而业务上又必须要求一致性,因此单个用户的余额,商品库存等只能单点写入,然后让其他节点做备份,无法做到分布式情况下的多点写入。

fig3.png

这种设计的问题在于某个节点故障的时候,这个节点上的用户就无法进行读写操作了,但站在整体上来看,这种设计可以降低故障时受影响的用户的数量和范围。

1.2.3 分区处理

如果出现了分区,那么就需要在分区期间记录足够多的日志,当分区故障解决之后,系统根据日志进行数据恢复,使得重新达到CA的状态

1.3 BASE

  • basically available 基本可用
    • 出现故障的时候,允许损失部分可用性,即保证核心可用
  • soft state 软状态
    • 允许系统存在中间状态,而该中间状态不会影响系统的整体可用性。这里的中间状态是CAP理论中的数据不一致
  • eventual consistency 最终一致性
    • 系统的所有数据副本经过一定时间之后,最终能够达到一致的状态。

2.3 FMEA分析表

2.3.1 功能点

  • 从用户角度看涉及到的功能点
    • 用户角度,即登录注册这种功能
    • 而不是数据库缓存这种

2.3.2 故障模式

  • 系统会出现什么样的故障
    • 包括故障点
    • 故障模式
      • 假设某种故障现象即可
  • 故障模式的描述要尽量精确,多使用量化的值来进行描述,避免泛化的方式。比如说慢,慢到3秒比说单纯的慢要好得多。

2.3.3 故障影响

当发生故障模式中描述的故障时,功能点具体会受到什么影响。常见的影响有:

  • 功能点完全不可用
  • 部分不可用
  • 响应缓慢
  • 功能出错

故障影响也应该尽量准确描述,比如可能影响20%的用户之类的。

2.3.4 严重程度

站在业务的角度来看故障的影响程度,一般分为致命/ 高/ 中/ 低/ 无 五个档次。

严重程度 = 功能点重要程度 x 故障影响范围 x 功能点受损程度

2.3.5 故障原因

列出故障原因,是为了

  • 故障原因都具有不同的概率,这会影响我们的解决方案
  • 不同故障原因检测手段不同
  • 处理措施不同

2.3.6 故障概率

  • 硬件
  • 开源系统
  • 自研系统

2.3.7 风险程度

风险程度 = 严重程度 x 故障概率

2.3.8 已有措施

  • 检测告警
    • 检测故障,告警,人工干预
  • 容错
    • 系统通过备份手段来应对
  • 自恢复
    • 检测到故障以后,系统能够自动恢复。比如,Hadoop检测到某台机器故障之后,将存储在这台机器的副本重新分配到别的机器上。

2.3.9 规避措施

  • 技术手段
    • 冗余备份等
  • 管理手段
    • 硬件定期更新

2.3.10 解决措施

为了能够解决问题而给出的方案,往往是技术手段

2.3.11 后续规划

综合前面的分析,就可以看出哪些故障我们目前还缺乏对应的措施,哪些已有措施还不够,针对这些不足的地方,再结合风险程度进行排序,给出后续的改进规划。这些规划既可以是技术手段,也可以是管理手段;可以是规避措施,也可以是解决措施。同时需要考虑资源的投入情况,优先将风险程度高的系统隐患解决。

3. 高可用存储架构

存储高可用方案 – 本质 – 将数据复制到多个存储设备当中,通过数据冗余的方式来实现高可用, 其复杂性主要体现在如何应对复制延迟和中断导致的数据不一致的问题。因此,对任何一个高可用存储方案,我们需要考虑以下几个问题:

  • 数据如何复制的
  • 各个节点的职责是什么
  • 如何应对复制延迟
  • 如何应对复制中断

3.1 双机架构

3.1.1 主备复制

fig4.jpg

主备架构的备机起到一个备份的作用,并不承担实际的业务读写操作,如果将备机改为主机,是需要进行人工操作的。

  • 优势
    • 足够简单
    • 对于客户端来说,不需要感知备机的存在
    • 对于主备之间,只需要进行数据复制,无须进行状态判断和主备切换这类复杂操作
  • 劣势
    • 备份无读写的流量,因此硬件成本上是有浪费的
    • 故障之后需要人工干预,无法做自动恢复

3.1.2 主从复制

fig5.jpg

  • 优势
    • 主从复制在主机故障时,读操作相关的业务可以继续运行
    • 主从复制架构的从机提供读操作,发挥了硬件的性能
  • 劣势
    • 主从复制当中,客户端需要感知主从关系,并将不同的操作发给不同的机器进行处理,复杂度比主备的要高
    • 主从复制架构当中,从机提供读业务,如果主从复制延迟比较大,业务会因为数据不一致出现问题
    • 故障时需要人工干预

3.1.3 主备/ 主从切换

系统自动决定主机角色,并完成角色的切换。

以主备为例,需要考虑的问题有:

3.1.3.1 主备间状态判断

  • 状态传递渠道
    • 相互之间的连接
    • 第三方仲裁
  • 状态检测的内容
    • 机器是否掉电
    • 进程是否存在
    • 响应是否缓慢

3.1.3.2 切换决策

  • 切换时机
  • 切换策略
  • 自动程度

3.1.3.3 数据冲突解决

  • 需要做具体的应用场景的具体分析了

3.1.3.4 常见主备切换架构

  • 互连式
    • 主备之间进行状态传递
    • 缺点
      • 状态传递通道本身存在问题的话,备机也会自动升级为主机,造成有两个主机的结果
  • 中介式
    • 引入第三方中介,主备间不直接连接,而是去连接中介,并且通过中介来传递状态信息
    • 连接管理以及状态决策都更简单一些了
  • 模拟式
    • 主备之间不传递任何状态数据,而是备机模拟成一个客户端,向主机发起模拟的读写操作,根据读写操作的响应情况来判断主机的状态。

3.1.4 主主复制

fig6.png

  • 两台都是主机,不存在切换的概念
  • 客户端无须区分不同角色的主机,随便讲读写操作发送给哪台主机都是可以的
  • 双向复制本身的难以实现
    • 比如数据库同步,ID如何做更新等
    • 一般适合于临时性,可丢失,可覆盖的数据场景

3.2 集群和分区

3.2.1 数据集群

主备,主从,主主都有一个隐含的假设,主机能够存储所有数据。但是在实际场景当中,一台主机的存储和处理能力都是十分有限的,单台服务器一定是无法存储和处理的,我们必须使用多台服务器来存储数据,实现一个数据集群架构。

数据集群又可以分为数据集中集群,数据分散集群

3.2.1.1 数据集中集群

fig7.png

客户端与主机进行交流,主机进行数据复制到各个备机上去。Zookeeper解决了大部分数据集中集群中会出现的问题。

  • 多条复制通道,会增大主机复制的压力,需要考虑如何降低主机复制压力
  • 多条复制通道会导致多个备机之间数据不一致,我们需要对备机之间的数据一致性进行检查和修正
  • 备机如何检查主机的状态,如何处理不同备机对主机状态的不同判断
  • 主机故障以后,如何决定新的主机

3.2.1.2 数据分散集群

  • 多个服务器组成一个集群,每台服务器都会负责存储一部分数据;同时为了提升硬件利用率,每台服务器又会备份一部分数据。

  • 数据分配算法需要考虑到:

    • 均衡性 – 数据分区,数据容量基本上相同
    • 容错性 – 当服务器故障时,算法需要将原来分配给故障服务器的数据分区分配给其他服务器
    • 可伸缩性 – 集群容量不够,扩充了新的服务器之后,算法能够自动将部分数据分区迁移到新服务器上,并保证扩容后所有服务器的均衡性
  • 需要有一台机器来执行数据分配算法

3.2.2 数据分区

针对于影响很大的事故或者灾难来说,有可能会使得所有硬件全部故障。数据分区指的是按照一定规则进行分区,分布到不同的地理位置上,每个分区存储一部分数据,通过这种方式来规避地理级别的故障所造成的的影响。

  • 数据分区的考虑因素
    • 数据量
    • 分区规则
    • 复制规则
      • 集中式
        • 总的备份中心,所有分区将数据备份到备份中心
        • 设计简单,可互不影响
        • 扩展容易
        • 成本较高,需要建立一个独立的备份中心
      • 互备式
        • 每个分区备份另外一个分区的数据
        • 复杂度高,相互之间强关联
        • 成本低
      • 独立式
        • 每个分区都有自己的独立的备份中心
        • 扩展容易,设计简单
        • 成本很高

4. 计算高可用架构

当部分硬件损坏时,计算任务能够继续正常运行。因此计算高可用的本质是通过冗余来规避部分故障的风险。即通过增加更多的服务器来达到计算高可用。

设计复杂度主要体现在任务管理上,即当任务在某台服务器上执行失败以后,如何将任务重新分配到新的服务器进行执行。

4.1 哪些服务器可以执行任务

  • 特定服务器
  • 每个服务器

4.2 任务如何重新执行

  • 对已经分配的任务即使执行失败也不做任何处理,系统只需要保证新的任务能够分配到其他非故障的服务器上执行即可
  • 设计一个任务管理器来管理需要执行的计算任务,服务器执行任务后,需要向任务管理器反馈任务执行的结果,任务管理器根据任务执行结果来决定是否需要将任务重新分配到另外的服务器上执行。

4.3 常见的计算高可用架构

4.3.1 主备

fig8.png

  • 和存储高可用类似,不过更简单,因为不需要做数据复制的
  • 主机执行所有计算任务
  • 主机故障,任务分配器不会自动将计算任务发给备机,系统此时是不可用状态
  • 如果主机恢复(人工或自动),任务分配器继续将任务发送给主机
  • 如果主机不能够恢复,则需要人工操作,将备机升为主机,然后让任务分配器将任务发送给新的主机

4.3.2 主从

fig9.png

  • 主机执行部分计算任务,备机也执行一部分
  • 主机故障时,任务分配器还是会发给主机
  • 不能恢复,人工操作,备机变主机,增加新的备机
  • 好处是从机执行任务,发挥了硬件性能;缺点是需要将任务分类,任务分配器会复杂一些

4.3.3 集群

系统需要能够自动完成切换操作,这是高可用集群方案。根据节点的角色,可以分成对称集群,集群内的每个服务器都有一样的角色,都可以执行所有的任务;另一类是非对称集群,集群中服务器分为多个不同角色,执行不同的任务。

4.3.3.1 对称集群 - 负载均衡集群

fig10.png

  • 任务分配器采取某种策略(随机、轮询等)将计算任务分配给集群当中的不同服务器
  • 当集群中的某台服务器出现故障以后,任务分配器不再将任务分配给它,而是将任务分配给其他服务器执行
  • 当故障的服务器恢复之后,任务分配器重新将任务分配给它执行
  • 难点
    • 分配策略
      • 轮询
      • 随机
    • 检测服务器状态
      • 服务器本身状态
        • 是否宕机
        • 网络是否正常
      • 任务执行状态
        • 任务卡死
        • 执行时间过长等
      • 一般来说是通过在任务分配器和服务器之间通过心跳来传递信息,包括服务器信息和任务信息,然后根据实际情况来确定状态,判断条件

4.3.3.2 非对称集群

  • 集群通过某种方式来区分不同的服务器角色
    • ZAB 算法
  • 当指定类型的服务器故障时,需要重新分配角色
  • 比均衡负载更复杂
    • 任务分配策略更加复杂 - 需要将任务划分为不同类型并分配给不同角色的集群节点
    • 角色分配策略实现更复杂

5. 异地多活架构

  • 异地 - 地理位置上的不同
  • 多活 - 不同地理位置上的系统都能提供业务服务
  • 异地多活标准
    • 用户无论访问哪一个地点的业务系统,都能够得到正确的业务服务
    • 某个地方业务异常的时候,用户访问其他地方正常的业务系统,能够得到正确的业务服务
  • 异地多活实现代价非常高
    • 系统复杂度显著上升
    • 成本上升
  • 需要异地多活的场景
    • 滴滴
    • 微信
    • 支付宝等

5.1 架构模式

5.1.1 同城异区

将业务部署在同一个城市不同区的多个机房当中。虽然还是无法在地震海啸等大灾害面前存活,但是同城异区通过光纤的设定,可以实现几乎和同一个机房相同的网络传输速度。这样尽管是多个机房,但我们可以将其作为一个机房来看待。

还是个看概率的问题,和地震海啸相比,机房火灾停电这种事情更有可能发生,对于此类故障,同城异区架构都可以很好地解决。

5.1.2 跨城异地

将业务部署在不同城市的多个机房当中。距离要足够远,以应对极端灾难事件。但是与之相对的是,两个机房就几乎无法实现同步了。

正因为无法同步,所以对于十分敏感的数据,比如说账户余额这类,就不会使用跨城异地这种方式了。

5.1.3 跨国异地

  • 主要为了为不同地区的用户提供服务
  • 或者是只读类业务做多活

5.2 异地多活设计技巧

5.2.1 保证核心业务的异地多活

因为保证所有业务的异地多活是不现实的,比如注册,登录,用户信息都要保持同步。注册不应该保持异地多活,因为一般都有单个手机号的限制,如果异地服务器,就无法检测是否已经注册过了,这是一个商业逻辑上的很大的悖论了,不可以这样做。

同理对于用户信息也是,根据更新时间的激活和识别也很可能会因为不同的机器时间的问题,而导致最终是不准确的,会带来很不好的用户体验。

登录才是最最需要保证异地多活的功能。

5.2.2 保证核心数据最终一致性

异地多活本质上是通过异地的数据冗余,来保证在极端的异常情况下,业务也能正常提供给用户。即需要实现数据的快速同步

  • 尽量减少异地多活机房的距离,搭建高速网络
  • 尽量减少数据同步,只同步核心业务相关的数据
  • 保证最终一致性,不保证实时的

5.3 如何应对接口级的故障

接口级故障的典型表现就是系统并没有宕机,网络也没中断,但业务却出现了问题。例如:

  • 业务响应缓慢
  • 访问大量超时
  • 大量访问出现异常

这类问题的主要原因在于系统压力太大,负载太高,导致无法快速处理业务请求,由此引发了更多的后续问题。常见的比如说数据库慢查询,将数据库的服务器资源耗尽了,导致读写超时,业务读写数据库要么超时,无法连接;从用户的角度来说,就是访问很慢,或者抛出异常。

5.3.1 接口级故障的原因

  • 内部原因
    • 程序bug导致死循环
    • 某个接口导致数据库慢查询
    • 程序逻辑不完善导致耗尽内存
  • 外部原因
    • 黑客攻击
    • 促销,抢购引入远超平时的用户
    • 第三方系统大量请求
    • 第三方系统响应缓慢
  • 解决的核心思想
    • 优先保证核心业务
    • 优先保证绝大部分用户

5.3.2 降级

系统将某些业务或者接口的功能降低,只提供部分功能或者完全停掉所有功能

  • 系统后门降级
    • 比如提供一个降级URL,当访问这个URL的时候,就相当于提供了一个降级操作
  • 独立降级系统
    • 独立出系统,实现权限管理批量操作等功能。

5.3.3 熔断

  • 和降级做对比
    • 降级是用来处理系统自身的故障
    • 熔断是应对依赖的外部系统的故障的情况
  • 熔断机制的关键在于一个统一的API调用层,由API调用层来进行采样或者统计
  • 另一个关键是阈值的设计,一般是根据分析确定阈值,然后上线观察效果,再进行调优

5.3.4 限流

从用户访问压力的角度来考虑,只允许系统能够承载的访问量来访问,超出系统访问能力的请求将被丢弃。

  • 基于请求限流
    • 常用方式
      • 限制总量
        • 比如限制总用户的上限
      • 限制时间量
        • 限制一段时间内某个指标的上限 比如请求tps
    • 这种方式更多是英语业务功能比较简单的系统,因为很可能阈值需要不断调整的
  • 基于资源限流
    • 找到系统内部影响性能的关键资源,对其使用上限进行限制
      • 连接数
      • 文件句柄
      • 线程数
      • 请求队列

5.3.5 排队

  • 不直接扔掉请求,排队
  • 排队模块
    • 将请求以先进先出的方式保存下来
  • 调度模块
    • 负责排队模块到服务模块的动态调度
  • 服务模块
    • 调用真正业务来处理服务,并返回处理结果,调用排队模块的接口回写处理结果。

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 stone2paul@gmail.com

文章标题:架构学习-高可用架构模式

文章字数:5.9k

本文作者:Leilei Chen

发布时间:2020-02-03, 20:21:29

最后更新:2020-02-03, 20:24:26

原始链接:https://www.llchen60.com/%E6%9E%B6%E6%9E%84%E5%AD%A6%E4%B9%A0-%E9%AB%98%E5%8F%AF%E7%94%A8%E6%9E%B6%E6%9E%84%E6%A8%A1%E5%BC%8F/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

目录
×

喜欢就点赞,疼爱就打赏