最近阅读了一本架构方面的入门图书叫《从零开始学架构:照着做,你也能成为架构师》,部分内容比较不错,先做书摘总结,以便加深印象与未来回顾学习。
本文是该书第三部分下半部分,是书中第九至十章,主要介绍计算高可用、业务高可用,涉及到异地多活等内容。
目录
▪第九章 计算高可用
▪第十章 业务高可用
▪其他相关摘要
第九章 计算高可用
- 计算高可用的主要涉及目标是当出现部分硬件损坏时,计算任务能够继续正常运行。因此,计算高可用的本质是通过冗余来规避部分故障的风险。
- 常见的计算高可用架构有:主备、主从、对称集群、非对称集群。
- 计算高可用架构的设计复杂度主要体现在任务管理方面。关键点有:哪些服务器可以执行任务、任务如何重新执行。需要注意的是:任务管理中经常出现的“任务分配器”是一个逻辑的概念,并不一定要求系统存在一个独立的任务分配器模块。
- 主备:主备架构是计算高可用最简单的架构,和存储高可用的主备复制架构类似,但要更简单,因为不需要数据复制。 根据备机状态的不同,主备架构又可以细分为冷备架构和温备架构。冷备可以节省一定的能源,但温备能够大大减少手工操作时间,因此一般情况下推荐用温备的方式。 主备架构的有点就是简单,主备机之间不需要进行交互,状态判断和倒换操作由人工执行,系统实现很简单。而缺点正好也体现在“人工操作”这点上。和存储高可用中的主备复制架构类似,计算高可用的主备架构也比较适合于内部管理系统、后台管理系统这类使用人数不多,使用频率不高的业务,不太适合在线的业务。
- 主从:和存储高可用的主从复制架构类似,计算高可用的主从架构中的从机也是要执行任务的。任务分配器需要将任务进行分类,确定哪些任务可以发送给主机执行,哪些任务可以发送给备机执行。 由于主从架构从机也执行任务,发挥了从机的硬件性能。但主从架构需要将任务分类,任务分配器会复杂一些。
- 对称集群:主备架构和主从架构通过冗余一台服务器来提升可用性,且需要人工来切换主备或主从。但在可用性要求更严格的场景中,我们需要系统能够自动完成切换操作,这就是高可用集群方案。高可用计算的集群根据集群中服务器节点角色的不同,可以分为两类:一类是对称集群,即集群中每个服务器的角色都是一样的,都可以执行所有任务;一类是非对称集群,集群中的服务器分为多个不同的角色,不同的角色执行不同的任务。而对称集群更通俗的叫法是负载均衡集群。 负载均衡集群的设计关键点在于两点:任务分配器需要检测服务器状态,任务分配器需要选取分配策略。任务分配策略比较简单,轮询和随机基本就够了;状态检测稍微复杂一些,既要监测服务器的状态,例如服务器是否宕机、网络是否正常等;同时还要检测任务的执行状态,例如任务是否卡死、是否执行时间过长等。常用的做法是任务分配器和服务器之间通过心跳来传递信息,包括服务器信息和任务信息,然后根据实际情况来确定状态判断条件。
- 非对称集群:其中不同服务器的角色是不同的,不同角色的服务器承担不同的职责。以Master-Slave为例,部分任务是Master服务器才能执行,部分任务是Slave服务器才能执行。
其设计复杂度主要体现在:
- 任务分配策略更加复杂:需要将任务划分为不同类型并分配给不同角色的集群节点。
- 角色分配策略实现比较复杂:可能需要使用Paxos这类复杂的算法来实现Leader的选举。
例如ZooKeeper,其任务分配器中不存在独立的任务分配器节点,每个Server都是任务分配器,Follower收到请求后会进行判断,如果是写请求就转发给Leader,如果是读请求就自己处理。而角色指定上,其通过ZAB协议来选举Leader,当Leader故障后,所有的Follower节点会暂停读写操作,开始进行选举,直到新的Leader选举出来后才继续对Client提供服务。
如果非对称集群只有两台机器,看起来和存储高可用中的主从倒换方案类似。但在计算高可用非对称集群中,我们并不把这种方案独立出来,因为2台服务器的非对称集群和3台或100台服务器的集群并没有设计上的本质差异。
第十章 业务高可用
- 异地多活
- 如果业务期望达到即使在天灾人祸等灾难性故障的情况下,业务也不受影响,或者在几分钟内就能够很快恢复,那么就需要设计异地多活架构。
- 异地多活架构的关键点就是异地、多活,其中异地就是指地理位置上不同的地方,类似于“不要把鸡蛋都放在同一个篮子里”;多活就是指不同地理位置上的系统都能够提供业务服务,这里的“活”是活动、活跃的意思。
- 判断一个系统是否符合异地多活的标准:
- 正常情况下,用户无论访问哪一个地点的业务系统,都能够得到正确的业务服务。
- 某地系统异常情况下,用户访问到其他地方正常的业务系统,也能够得到正确的业务服务。
- “备”是备份,正常情况下对外是不提供服务的,如果需要提供服务,则需要大量的人工干预和操作,花费大量的时间才能让“备”变成“活”。
- 实现异地多活架构有很高的代价:
- 系统复杂度会发生质的变化,需要设计复杂的异地多活架构;
- 成本会上升,毕竟要多在一个或多个机房搭建独立的一套业务系统。
- 像共享单车、滴滴出行、支付宝、微信这类业务,就需要做异地多活了。这类业务系统中断后,对用户的影响很大。当然,如果业务规模很大,能做异地多活的情况下尽量实现异地多活。
- 异地多活架构可以分为同城异区、跨城异地、跨国异地。
- 同城异区:这虽然是两个不同地理位置上的机房,但可通过搭建高速网络能够实现和同一个机房内几乎一样的网络传输速度,因此逻辑上可以将他们看作同一个机房,这样的设计大大降低了复杂度,减少了异地多活的设计和实现复杂度及成本。但如果发生了本地区的自然灾害却无能为力。结合复杂度、成本、故障发生概率来综合考虑,同城异区是应对机房级别故障的最优架构。 其关键在于搭建高速网络将两个机房连接起来,达到近似一个本地机房的效果。架构设计上可以将两个机房当做本地机房来设计,无需额外考虑。
- 跨城异地:业务部署在不同城市的多个机房,而且距离最好远一些。跨城异地是为了解决天灾人祸问题的,因此需要在距离上比较远,才能有效应对这类极端灾难事件。 然而距离增加带来的最主要原因是两个机房的网络传输速度会降低,而且跨城异地肯定会导致数据不一致,因此需要根据数据的特性来做不同的架构。所以支付宝等金融相关的系统,对余额这类数据,不能做跨城异地的多活架构,而只能采用同城异区的这种架构。 而对数据一致性要求不那么高,或者数据不怎么改变,或者即使数据丢失影响也不大的业务,跨城异地多活就能够派上用场了。例如,用户登录(数据不一致用户重新登录即可)、新闻类网站(一天内的新闻数据变化少)、微博类网站(某地丢失用户发布的微博或评论影响不大)这些业务采用跨城异地多活,就能够很好地应对某地区极端灾难的场景。 其关键在于数据不一致的情况下,业务不受影响或影响很小,这从逻辑的角度上来说其实是矛盾的,架构设计的主要目的就是为了解决这个矛盾。
- 跨国异地:这种多活的主要应用场景一般是为不同地区用户提供服务以及只读类业务做多活这种情况。对架构设计要求不高。
- 异地多活设计技巧
- 保证核心业务的异地多活。例:模拟案例中登录为核心业务,而不是要求数据一致性的注册。
- 核心数据最终一致性:
- 尽量减少异地多活机房的距离,搭建高速网络;
- 尽量减少数据同步,只同步核心业务相关数据;
- 保证最终一致性,不保证实时一致性:最终一致性就是在介绍CAP理论时提到的BASE理论,即业务不依赖数据同步的实时性,只要数据最终能一致即可。
- 采用多种手段同步数据: 虽然MySQL的主备复制、Redis的Cluster功能、Elasticsearch的集群功能等存储系统本身自带了同步功能,但在某些场景下是无法满足我们也无需要的。尤其是异地多机房部署各种各样的异常情况都可能出现,当我们只考虑存储系统本身的同步功能时,就会发现无法做到真正的异地多活。解决方案就是拓展思路,避免只使用存储系统的同步功能,可以将多种手段配合存储系统的同步来使用,甚至可以不采用存储系统的同步方案,改用自己的同步方案。 同步数据方式包括但不限于:消息队列、二次读取、存储系统同步、回源读取、重新生成数据等
- 只保证绝大部分用户的异地多活。这里承认了业务无法百分百可用,因此为了让用户心里好受,我们可以采取挂公告、事后对用户进行补偿、补充体验等操作进行安抚或补偿。
- 异地多活设计步骤
- 业务分级:按照一定标准对业务进行分级,挑选核心业务,只为核心业务设计异地多活。常见分级标准:
- 访问量大的业务;
- 核心业务;
- 产生大量收入的业务。
- 数据分类:对核心业务相关的数据进一步分析,目的在于识别所有的数据及数据特征,这些数据特征会影响后面的方案设计。常见数据特征分析维度:
- 数据量:包括总的数据量和新增、修改、删除的量;
- 唯一性:数据是否要求多个异地机房产生的同一类数据必须保证唯一。如果数据要求必须唯一,要么只能一个中心点产生数据,要么需要设计一个数据唯一生成的算法;
- 实时性:如果在A机房修改了数据,要求多长时间必须同步到B机房,实时性要求越高,对同步的要求越高,方案越复杂。
- 可丢失性:数据是否可以丢失。
- 可恢复性:数据丢失后,是否可以通过某种手段进行恢复,如果数据可以恢复,至少说明对业务影响不那么大,这样可以相应降低异地多活架构设计的复杂度。
- 数据同步:常见数据同步方案有:
- 存储系统同步:最常用也是最简单的同步方式。例如MySQL的主从同步、主主同步。优点是使用简单,缺点是同步方案都是通用的,无法针对业务数据特点做定制化的控制。
- 消息队列同步:采用独立消息队列进行数据同步,常见的消息队列有Kafka、ActiveMQ、RocketMQ等。消息队列同步适合无事务性或无时序性要求的数据。例如对于新注册的用户账号,可以采用消息队列同步;而对于用户密码,就不能采用消息队列通不了。
- 重复生成:数据不同步到异地机房,每个机房都可以生成数据,这个方案适合于可以重复生成的数据。例如登陆产生的cookie、session数据以及缓存数据等。
- 异常处理:异常处理主要是在问题发生时,避免少量数据异常导致整体业务不可用;问题解决后,将异常的数据进行修正;对用户进行安抚,弥补用户损失。常见的异常处理措施有:
- 多通道同步:采取多种方式进行数据同步,可以应对同步通道出故障的情况。例如针对用户账号数据同步可有MySQL主从同步和消息队列同步两种方式。方案设计关键点有:
- 一般情况采取两通道即可;
- 数据库同步通道和消息队列同步通道不能采用相同的网络连接,可以一个走公网,一个走内网。
- 数据是可以重复覆盖的。例如新建账号数据符合此标准,但密码数据不符合此标准。
- 同步和访问结合:异地机房通过系统的接口来进行数据访问。方案设计关键点有:
- 数据库同步通道和接口访问通道不能采用相同的网络连接,可以接口走公网,数据库同步走内网。
- 数据有路由规则,可以根据数据来推断应该访问哪个机房的接口来读取数据。
- 由于有同步通道,优先读取本地数据,本地数据无法读取到再通过接口去访问,这样可以大大降低跨机房的异地接口访问数量,适合于实时性要求非常高的数据。
- 日志记录:常见日志保存方式有:
- 服务器上保存日志,数据库中保存数据,这种方式可以应对单台数据库服务器故障或宕机的情况。
- 本地独立系统保存日志,这种方式可以应对某业务服务器和数据库同时宕机的情况。例如,服务器和数据库部署在同一个机架,或者同一个电源线路上,就会出现服务器和数据库同时宕机的情况。
- 日志异地保存,这种方式也可以应对机房宕机的情况。
- 用户补偿:例如《炉石传说》2017年回档故障,暴雪给予每个用户大约价值人民币200元的补偿(25卡包)。
- 多通道同步:采取多种方式进行数据同步,可以应对同步通道出故障的情况。例如针对用户账号数据同步可有MySQL主从同步和消息队列同步两种方式。方案设计关键点有:
- 业务分级:按照一定标准对业务进行分级,挑选核心业务,只为核心业务设计异地多活。常见分级标准:
- 接口级故障应对方案
接口级故障的典型表现:系统并没有宕机,网络没有中断,但业务出现问题。例如业务响应缓慢、大量访问超时、大量访问出现异常(弹窗“无法连接数据库”),这类问题的主要原因在于系统压力太大,负载太高,导致无法快速处理业务请求,由此引发更多的后续问题。
导致接口级故障的原因:
- 内部原因:程序bug导致死循环、某个接口导致数据库慢查询、程序逻辑不完善导致耗尽内存等。
- 外部原因:黑客攻击、促销或抢购引入了超出平时几倍甚至几十倍的用户、第三方系统大量请求、第三方系统响应缓慢等。
解决接口级故障的核心思想和异地多活基本类似:优先保证核心业务,优先保证绝大部分用户。
具体措施:
- 降级:核心思想是丢车保帅,优先保证核心业务:
- 系统后门降级:系统预留后门进行降级操作。具体降级指令通过URL参数传入,有一定安全隐患,徐艳在URL中加入密码这类安全措施。其实现成本比较低,但若服务器数量多,需要一台台操作,效率较低。
- 独立降级系统:将降级操作独立到一个单独的系统中,可以实现复杂的权限管理、批量操作等功能。
- 熔断:应对依赖的外部系统故障。实现的关键是需要有一个统一的API调用层,由API调用层来进行采样或统计;另外还有阈值的设计,实践中一般先根据分析确定阈值,然后上线观察效果,最后进行调优。
- 限流:从用户访问压力的角度考虑如何应对故障。只允许系统能够承受的访问量进来,超出系统访问能力的请求将被丢弃。限流一般都是系统内实现的。常见的限流方式分类:
- 基于请求限流:从外部访问的请求角度考虑限流,方式有:
- 限制总量:限制某个指标的累计上限,常见是限制当前系统服务的用户总量。
- 限制时间量:限制一段时间内某个指标的上线。
这种方式实践中比较难以找到合适的阈值。即使找到合适阈值,其还面临硬件相关问题,例如64核及其对比32核及其业务处理性能并不是2倍,可能是1.5甚至1.1倍。
为了找到合理阈值,通常可以采用性能压测来确定阈值,但仍存在覆盖场景有限的问题。另外还可以进行逐步优化,即先设定一个阈值然后上线观察运行情况,发现不合理就调整阈值。
- 基于资源限流:常见的内部资源有连接数、文件句柄、线程数、请求队列等。也可以根据CPU的负载或占用率进行限流,当CPU的占用率超过80%的时候就开始拒绝新的请求。 其实践中设计面临两个主要难点:如何确定关键资源和如何确定关键资源的阈值,但这也是一个逐步调优的过程。
- 基于请求限流:从外部访问的请求角度考虑限流,方式有:
- 排队:实际上是限流的一个变种。排队虽然没有直接拒绝用户,但用户等了很长时间后进入系统,体验并不一定比限流好。 由于排队需要临时缓存大量的业务请求,单个系统内部无法缓存这么多数据,一般情况下,排队需要用独立的系统去实现。例如,使用Kafka这类消息队列来缓存用户请求。
其他相关摘要
- 存储高可用数据集群中涉及到的两个算法:
- 分布式事务算法:其主要目的是为了保证分散在多个节点上的数据统一提交或回滚。有二阶段提交(Two-phase commit protocol, 2PC)、三阶段提交(Three-phase commit protocol, 3PC)两个著名主流算法。
- 二阶段提交(Two-phase commit protocol, 2PC)
二阶段提交算法主要由两个阶段组成:commit请求阶段、commit提交阶段。其成立基于以下假设:
- 在分布式系统中,存在一个节点作为协调者(Coordinator),其他节点作为参与者(Cohorts),且节点之间可以进行网络通信。
- 所有节点都采用预写式日志,且日志被写入后即保持在可靠的存储设备上,即使节点损坏,也不会导致日志数据的消失。
- 所有节点不会永久性损坏,即使损坏,仍然可以恢复。
- 二阶段提交(Two-phase commit protocol, 2PC)
二阶段提交算法主要由两个阶段组成:commit请求阶段、commit提交阶段。其成立基于以下假设:
2PC是强一致性算法,优点是实现简单,缺点有同步阻塞,即协调者与参与者互相等待对方的响应消息,等待过程中节点处于阻塞状态,所以极端情况下无论怎么处理都可能出现状态不一致的情况;另外还有单点故障,即协调者是整个算法的单点,如果协调者故障,则参与者会一直阻塞下去。
- 三阶段提交(Three-phase commit protocol, 3PC) 三阶段提交算法是针对二阶段提交算法在“单点故障”产生的问题所提出的解决方案。通过在二阶段提交算法中的第一阶段和第二阶段之间插入一个新的阶段“准备阶段”,当协调者故障后,参与者可以通过超时提交来避免一直阻塞。 三阶段提交算法虽然避免了二阶段提交算法的协调者单点故障导致系统阻塞的问题,但同样存在数据不一致问题。
- 分布式一致性算法:主要目的是为了保证同一份数据在多个节点上的一致性,以满足CAP中的CP要求。
复制状态机是实现分布式一致性的常用技术,其主要角色有:副本、状态机、算法。复制状态机的核心就是分布式一致性算法,其都比较复杂。
- Paxos
Paxos是被理论上证明为正确的算法(另外一个被数学家理论上证明的定理是CAP理论),但Paxos存在着理论特别复杂难以理解、确实很多细节难以实现的明显问题。
因此在工程实践中Paxos算法一般步骤是:
- 以Paxos算法为基础,开始尝试实现。
- 发现有的地方很难实现,或者没有明确规定如何实现,于是想了一个方案去实施。
- 最终看起来完成了Paxos算法,但算法的正确性已经无法完全保证,可能在某些场合下算法达不到一致性的目的。
理解Paxos算法有几个关键点:
- Paxos算法是多数一致性(Quorum),不是全体一致性。
- Client发起的请求可以是任何操作,而不仅限于写操作,读操作也可以。
- Paxos算法中的角色(Proposer、Acceptor、Learner、Leader)是逻辑上的划分,不是集群上的物理节点要这样划分,事实上实现的时候比较灵活。
- Raft Raft算法通过将分布式一致性问题拆分为3个子问题来简化算法:Leader选举、日志复制、安全保证;Raft强化了Leader的作用,通过Leader来保证分布式一致性。
- ZAB ZAB全称是ZooKeeper Atomic Broadcast Protocol,是ZooKeeper系统中采用的分布式一致性算法。抛开各种实现细节,ZAB的实现和Raft其实是相似 ,例如,强化Leader的作用,通过Leader来保证分布式一致性。ZAB肯定是Paxos的一个不完整版实现。 ZAB同Paxos和Raft有一个交大的差异是复制方式,Paxos和Raft采用的是State Machine Replication (又称为Active Replication),ZAB采用的是Primary Backup (又称为Passive Replication),两种实现方式差异前者为各个节点复制的是具体的操作,然后每个节点自己执行操作;后者为Leader节点执行操作,将执行结果复制给其他节点。
- Paxos
Paxos是被理论上证明为正确的算法(另外一个被数学家理论上证明的定理是CAP理论),但Paxos存在着理论特别复杂难以理解、确实很多细节难以实现的明显问题。
因此在工程实践中Paxos算法一般步骤是:
- 分布式事务算法:其主要目的是为了保证分散在多个节点上的数据统一提交或回滚。有二阶段提交(Two-phase commit protocol, 2PC)、三阶段提交(Three-phase commit protocol, 3PC)两个著名主流算法。