架构师修炼系列【业务高可用】

无论高可用计算架构,还是高可用存储架构,其本质的设计目的都是为了解决部分服务器 故障的场景下,如何保证系统能够继续提供服务。但在一些极端场景下,有可能出现所有服务 器都出现故障,如果业务期望达到即使在此类灾难性故障的情况下,业务也不受影响,或者在几分钟内就能够很快恢复,那么就需要设计异地多活架构

判断一个系统是否符合异地多活,需要满足如下两个标准:

  • 正常情况下,用户无论访问哪一个地点的业务系统,都能够得到正确的业务服务
  • 某地系统异常情况下,用户访问到其他地方正常的业务系统,也能够得到正确的业务服务

实现异地多活架构是有很高的代价的,具体表现为:

  • 系统复杂度会发生质的变化,需要设计复杂的异地多活架构
  • 成本会上升,毕竟要多在一个或多个机房搭建独立的一套业务系统

异地多活架构

同城异区

同城异区指的是将业务部署在同一个城市不同区的多个机房,同城的两个机房,距离上一般大约就是几十千米,通过搭建高速的网络,同城异区的两个 机房能够实现和同一个机房内几乎一样的网络传输速度。这就意味着虽然是两个不同地理位置 上的机房,但逻辑上我们可以将它们看作同一个机房,这样的设计大大降低了复杂度,减少了异地多活的设计和实现复杂度及成本

跨城异地

跨城异地指的是业务部署在不同城市的多个机房,而且距离最好要远一些,跨城异地虽然能够有效应对极端灾难事件但“距离较远”这点并不只是一个距离数字上 的变化,而是量变引起了质变,导致了跨城异地的架构复杂度大大上升。距离增加带来的最主 要原因是两个机房的网络传输速度会降低,远不是以人的意志为转移的,而是物理定律决定的,即光速真空传播是每秒 30 万千米,在光纤中传输的速度大约是每秒 20 万千米,再加上传输中 的各种网络设备的处理,实际还远远达不到光速的速度
跨城异地距离较远带来的网络传输延迟问题,给业务多活架构设计带来了复杂性,如果要 做到真正意义上的多活,业务系统需要考虑部署在不同地点的两个机房。在数据短时间不一致 的情况下,还能够正常提供业务 。 这就引入了 一个看似矛盾的地方:数据不一致业务肯定不会正常,但跨城异地肯定会导致数据不一致

跨过异地

跨国异地指的是业务部署在不同国家的多个机房。相比跨城异地,跨国异地的距离更加远 了,因此数据同步的延时会更长,正常情况下可能就有几秒钟了。这种程度的延迟己经无法满 足异地多活标准的第一条 : “正常情况下,用户无论访问哪一个地点的业务系统,都能够得到正 确的业务服务”虽然跨城异地也会有此类同步延时问题,但正常情况下几 十毫秒的延时对用户来说基本无感知的;而延时达到几秒钟就感觉比较明显了
跨国异地 多活的主要应用场景一般有如下几种情况 :

  • 为不同地区用户提供服务
  • 只读类业务做多活

异地多活设计思想

  • 同城异区:关键在于搭建高速网络将两个机房连接起来,达到近似一个本地机房的效果。架构设计 上可以将两个机房当作本地机房来设计,无须额外考虑
  • 跨城异地:关键在于数据不一致的情况下,业务不受影响或影响很小,这从逻辑的角度上来说其实 是矛盾的,架构设计的主要目的就是为了解决这个矛盾
  • 跨国异地:主要是面向不同地区用户提供业务,或者提供只读业务,对架构设计要求不高

跨城异地多活是架构设计复杂度最高的一种架构,然而它还是有一些设计技巧的:

  • 技巧一:保证核心业务的异地多活而非全部业务的异地多活

在这里插入图片描述

  • 技巧二 :核心数据最终一致性,而非全部数据实时同步
    异地多活本质上是通过异地的数据冗余,来保证在极端异常的情况下业务也能够正常提供 给用户,因此数据同步是异地多活架构的设计核心,异地多活理论上就不可能很快,因为这是物理定律决定的,处理技巧:

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

数据同步是异地多活架构设计的核心 ,基本上存储系统本身都会有同步的功能,例如, MySQL 的主备复制、Redis的Cluster功能、Elasticsearch 的集群功能 。 这些系统本身的 同步功能已经比较强大,能够直接拿来就用 ,但这也无形中将我们引入 了 一个思维误区 : 只使 用存储系统的同步功能

解决的方案就是拓展思路,避免只使用存储系统的同步功能,可以将多种手段配合存储系 统的同步来使用,甚至可以不采用存储系统的同步方案,改用自己的同步方案:

  • 消息队列方式:对于账号数据,由于账号只会创建,不会修改和删除(假设我们不提供删除功能),我 们可以将账号数据通过消息队列同步到其他业务中心
  • 二次读取方式:某些情况下可能出现消息队列同步也延迟了,用户在 A 中心注册,然后访问 B 中心的 业务,此时 B 中心本地拿不到用户的账号数据。为了解决这个问题, B 中心在读取本地 数据失败时,可以根据路由规则,再去 A 中心访问一次(这就是所谓的二次读取,第 一次读取本地,本地失败后第二次读取对端),这样就能够解决异常情况下同步延迟的问题
  • 存储系统同步方式:对于密码数据,由于用户改密码频率较低,而且用户不可能在 ls 内连续改多次密码, 所以通过数据库的同步机制将数据复制到其他业务中心即可,用户信息数据和密码类似。
  • 回源读取方式:对于登录的session数据,由于数据量很大,我们可以不同步数据;但当用户在 A 中心 登录后,然后又在B中心登录, B中心拿到用户上传的session id后,根据路由判断session属于A中心,直接去A中心请求session数据即可,反之亦然, A中心也可以到B中心去获取 session 数据
  • 重新生成数据方式:对于第4种场景 ,如果异常情况下, A中心宕机了, B中心请求session数据失败,此时就只能登录失败,让用户重新在B中心登录 ,生成新的session数据
    在这里插入图片描述
  • 技巧四:只保证绝大部分用户的异地多活即99.99%而非100%,因为它不可能

异地多活设计步骤

业务分级

按照一定的标准将业务进行分级,挑选出核心的业务,只为核心业务设计异地多活,降低方案整体复杂度和实现成本,常见分级标准:

  • 访问量大的业务
  • 核心业务
  • 产生大量收入的业务

数据分类

挑选出核心业务后,需要对核心业务相关的数据进行进一步分析,目的在于识别所有的数据及数据特征,这些数据特征会影响后面的方案设计,常见的数据特征分析维度:

  • 数据量:这里的数据量包括总的数据量和新增、修改、删除的量。 对异地多 活架构来说,新增、 修改、删除的数据就是可能要 同步的数据, 数据量越大, 同 步延迟的概率越高,同步方 案需要考虑相应的解决方案
  • 唯一性:唯一性指数据是否要求多个异地机房产生的同类数据必须保证唯一
  • 实时性:实时性指如果在 A 机房修改了数据 , 要求多长时间必须同步到 B 机房,实时性要求越高,对同步的要求越高,方案越复杂
  • 可丢失性:可丢失性指数据是否可以丢失。例如,写入 A 机房的数据还没有同步到 B 机房,此时 A 机房机器岩机会导致数据丢失 ,那这部分丢失的数据是否对业务会产生重大影响
  • 可恢复性:可恢复性指数据丢失后,是否可以通过某种手段进行恢复,如果数据可以恢复,至少说 明对业务的影响不会那么大,这样可以相应地降低异地多活架构设计的复杂度

数据同步

确定数据的特点后,我们可以根据不同的数据设计不同的同步方案。常见的数据同步方案:

  • 存储系统同步:这是最常用也是最简单的同步方式。例如 ,使用 MySQL 的数据主从数据同步、主主数据同步,缺点是这类同步方案都是通用的,无法针对业务数据特点做定制化的控制
  • 消息队列同步:采用独立消息队列进行数据同步,常见的消息队列有 Kafka 、 Acti veMQ 、RocketMQ 等,消息队列同步适合无事务性或无时序性要求的数据
  • 重复生成:数据不同步到异地机房,每个机房都可以生成数据,这个方案适合于可以重复生成的数 据。例如,登录产生的 c ookie 、 session 数据及缓存数据等

异常处理

无论数据同步方案如何设计,一旦出现极端异常的情况,总是会有部分数据出现异常的。 例如,同步延迟、数据丢失、数据不一致等。异常处理就是假设在出现这些问题时,系统将采取什么措施来应对。异常处理主要有以下几个目的:

  • 问题发生时,避免少量数据异常导致整体业务不可用
  • 问题恢复后,将异常的数据进行修正
  • 对用户进行安抚,弥补用户损失

常见的异常处理措施:

多通道同步

多通道同步的含义是采取多种方式来进行数据同步,其中某条通道故障的情况下 ,系统可以通过其他方式来进行同步,这种方式可以应对同步通道处故障的情况
在这里插入图片描述
多通道同步设计的方案关键点有如下几个 :
- 一般情况下,采取两通道即可,采取更多通道理论上能够降低风险,但付出的成本也会增加很多
- 数据库同步通道和消息队列同步通道不能采用相同的网络连接,否则一旦网络故障,两个通道都同时故障;可以一个走公网连接, 一个走内网连接
- 需要数据是可以重复覆盖的,即无论哪个通道先到哪个通道后到,最终结果是一样的,例如,新建账号数据就符合这个标准,而密码数据则不符合这个标准 。

同步和访问结合

这里的访问指异地机房通过系统的接口来进行数据访问 。 例如业务部署在异地两个机房A和B, B机房的业务系统通过接口来访问A机房的系统获取账号信息
在这里插入图片描述
同步和访问结合方案的设计关键点如下 :

  • 接口访问通道和数据库同步通道不能采用相同的网络连接,不能让数据库同步和接口访问都走同 一条网络通道,可以采用接口访问走公网连接,数据库同步走内网连接这种方式
  • 数据有路由规则,可以根据数据来推断应该访问哪个机房的接口来读取数据。例如,有3个机房A、B 、C, B机房拿到一个不属于B机房的数据后,需要根据路由规则判断是访问A机房接口,还是访问C机房接口
  • 由于有同步通道,优先读取本地数据,本地数据无法读取到再通过接口去访问,这样可 以大大降低跨机房的异地接口访问数量,适合于实时性要求非常高的数据

日志记录

日志记录主要用于故障恢复后对数据进行恢复,通过在每个关键操作前后都记录相关日志,然后将日志保存在一个独立的地方,当故障恢复后,拿出日志跟数据进行对比,对数据进行修复

为了应对不同级别的故障,日志保存的要求也不一样,常见的日志保存方式有如下几种 :

  • 服务器上保存日志,数据库中保存数据,这种方式可以应对单台数据库服务器故障或宕机的情况
  • 本地独立系统保存日志,这种方式可以应对某业务服务器和数据库同时看机的情况 。 例如,服务器和数据库部署在同一个机架,或者同一个电源线路上,就会出现服务器和数 据库同时右机的情况
  • 日志异地保存,这种方式可以应对机房者机的情况

以上不同方式,应对的故障越严重,方案本身的复杂度和成本就会越高,实际选择时需要综合考虑成本和收益情况

用户补偿

无论采用什么样的异常处理措施,都只能最大限度地降低受到影响的范围和程度,无法完全做到没有任何影响
例如,双同步通道有可能同时出现故障,日志记录方案本身日志也可能丢失。因此,无论多么完美的方案,故障的场景下总是可能有一小部分用户业务上出问题,系统无法弥补这部分用户的损失。但我们可以采用人工的方式对用户进行补偿,弥补用户损失,培养用户的忠城度。简单来说,系统的方案是为了保证99.99%的用户在故障的场景下业务不受影响,人工的补偿是为了弥补0 .01% 的用户的损失

接口级的故障应对方案

异地多活架构主要应对系统级的故障,但实际业务运行过程中,还有另外一种故障影 响可能没有系统级那么大,但发生的概率较高,这就是接口级的故障
导致接口级故障的原因 一般有如下几种 :

  • 内部原因:程序 bug 导致死循环,某个接口导致数据库慢查询,程序逻辑不完善导致耗尽内存等等
  • 外部原因 :黑客攻击、促销或抢购引入了超出平时几倍甚至几十倍的用户,第三方系统大量请求,第三方系统响应缓慢等等

解决接口级故障的核心思想和异地多活基本类似:优先保证核心业务 , 优先保证绝大部分用户

降级

降级指系统将某些业务或接口的功能降低,可以是只提供部分功能,也可以是完全停掉所 有功能,降级 的核心思想就是丢车保帅 ,优先保证核心业务

常见的 实现降级 的方式有如下几种:

  • 系统后门降级:简单来说, 就是系统预留了后门用于降级操作,系统后门降级的方式实现成本低,但主要缺点是如果服务器数量多,需要一 台 一 台去操作, 效率比较低,这在故障处理争分夺秒的场景下是比较浪费的
  • 独立降级系统:为了解决系统后 门 降级方式的缺点 ,我们将降级操作独立到一个单独的系统中,可以实现复杂的权限管理、批量操作等功能
    在这里插入图片描述

熔断

熔断和降级是两个比较容易混淆的概念,原因在于降级的目的是应对系统自身的故障,而熔断的目的是应对依赖的外部系统故障的情况
熔断机制实现的关键是需要有一个统一 的 API 调用层,由 API 调用层来进行采样或统计, 如果接口调用散落在代码各处就没法进行统一处理了
熔断机制实现的另外一个关键是阔值的设计,例如,1分钟内30%的请求响应时间超过1秒就熔断,这个策略中的“1分钟”“30%”“1秒”都对最终的熔断效果有影响 。实践中一般先根据分析确定阑值,然后上线观察效果, 最后进行调忧

限流

降级是从系统功能优先级的角度考虑如何应对故障,而限流则是从用户访问压力的角度来 考虑如何应对故障 。 限流指只允许系统能够承受的访问量进来,超出系统访问能力的请求将被丢弃
限流一般都是系统内实现的 ,常见的限流方式可以分为两类: 基于请求限流和基于资源限流

基于请求限流

基于请求限流指从外部访问的请求角度考虑限流,常见的方式有限制总量和限制时间量,限制总量的方式是限制某个指标的累积上限,常见的是限制当前系统服务的用户总量
无论限制总量 ,还是限制时间量,共同的特点都是实现简单,但在实践中面临的主要问题是比较难以找到合适的阈值

为了找到合理的阀值,通常情况下可以采用性能压测来确定阁值,但性能压测也存在覆盖 场景有限的问题,可能出现某个性能压测没有覆盖的功能导致系统压力很大;另外一种方式是 逐步优化, 即先设定一个阁值然后上线观察运行情况,发现不合理就调整阑值

基于上述的分析,根据阔值来限制访问量的方式更多适应于业务功能比较简单的系统,例如,负载均衡系统、网关系统、抢购系统等

基于资源限流

基于请求限流是从系统外部考虑的,而基于资源限流是从系统内部考虑 的,即找到系统 内 部影响性能的关键资源,对其使用上限进行限制。常见的内部资源有连接数、文件句柄 、线程数、请求队列等

例如,采用Netty来实现服务器,每个进来的请求都先放入一个队列,业务线程再从队列 读取请求进行处理,队列长度最大值为10000 ,队列满了就拒绝后面的请求 :也可以根据CPU的负载或占用率进行限流,当CPU的占用率超过80%的时候就开始拒绝新的请求

基于资源限流相比基于请求限流能够更加有效地反映当前系统的压力,但实践中设计也面临两个主要的难点:如何确定关键资源和如何确定关键资源的阁值 。通常情况下,这也是一个 逐步调优的过程,即设计的时候先根据推断选择某个关键资源和阀值,然后测试验证,再上线 观察,如果发现不合理,那么再进行优化

排队

排队实际上是限流的一个变种,限流是直接拒绝用户,排队是让用户等待很长时间
由于排队需要临时缓存大量的业务请求 ,单个系统内部无法缓存这么多数据,一般情况下, 排队需要用独立的系统去实现。例如,使用 Kafka 这类消息队列来缓存用户请求

  • 排队模块:负责接收用户的抢购请求 ,将请求以先入先出的方式保存下来,每一个参加秒杀活动的商局保存一个队列 , 队列的大小可以根据参与秒杀的商昂数量(或加点余量)自行定义
  • 调度模块:负责排队模块到服务模块的动态调度,不断检查服务模块, 一旦处理能力有空闲,就从排队队列头上把用户访问请求调入服务模块,并负责向服务模块分发请求 。这里调度模块扮演一个中介的角色, 但不只是传递请求而己,它还坦负着调节系统处理能力的重任。我们可以根据服务模块的实际处理能力,动态调节向排队系统拉取请求的速度
  • 服务模块:负责调用真正业务来处理服务,并返回处理结果,调用排队模块的接口回写业务处理结果

猜你喜欢

转载自blog.csdn.net/dawei_yang000000/article/details/108571248