导图社区 异地多活
异地多活,目的是为了实现高可用,提升可用性的关键是冗余。异地多活的演进,业界参考。有需要的小伙伴可以看看哦~
编辑于2022-07-08 12:24:22异地多活
目的
为了实现高可用
提升可用性的关键:冗余
冗余数据库、服务、副本、机房、云
演进
主流方案
风险点
所有服务部署在一个机柜或同一机房的多个机柜上,机房会有故障风险
解决方案
同城再冗余一个机房
同城灾备
B机房几乎镜像了一份A机房的所有部署,它们唯一的区别是:A机房的存储都是主库,而B机房都是从库
这种方案被称为热备,所谓热备是指B机房处于待命状态,A故障后B可以随时接管流量继续提供服务;而冷备则指B机房只做定期备份,不做实时同步,它会导致数据不完整、数据恢复期间业务不可用等。所以热备相比冷备的优势是:随时可切换
若A机房整个挂掉,我们需要:1.将B机房所有从库提升为主库2.将DNS指向B机房接入层,接入流量恢复业务
风险点
1.B机房只是作为灾备,并不确定其是否真的随时可切换2.B机房只是作为备份,太浪费资源了
解决方案
将B机房也接入流量用起来
同城双活
让B机房也接入流量,实时提供服务,这样做的好处:1.可实时训练这支后备军,让它达到与A机房相同的作战水平,以随时可切换2.B机房接入流量后,可分担A机房的流量压力3.不浪费资源
将B机房的接入层IP地址加入到DNS,这样B机房从上层就有流量进来了
B机房的存储只作为从库,是不可写的,这就需要业务应用做改造(一般通过中间件来实现):两个机房的读流量可打到任意机房的存储上,但写流量则只允许打到A机房存储,因为主库在A机房
MySQL、Redis、MQ等都需要区分读写请求,都需要做一定的改造(一般都通过中间件实现,业界也有一些开源组件,如Otter、redissyncer-server等)
两个机房部署在同城,物理距离比较近,而且两个机房用专线网络连接,所以虽然跨机房访问的延迟,比单个机房内要大一些,但整体的延迟是可以接受的
风险点
虽然我们把2个机房当做一个整体来规划,但这2个机房在物理层面上仍处于一个城市内,若整个城市发生自然灾害,这2个机房依旧存在全军覆没的风险
解决方案
在同城的2个机房之外再在异地冗余一个机房
所谓异地,通常建议两个机房的距离要在1000公里以上,这样才能应对城市级别的灾难,比如北京和上海
两地三中心
两地是指2个城市,三中心是指3个机房,其中2个机房在同一个城市,并同时提供服务,第3个机房部署在异地,只做数据灾备
这种架构方案,通常用在银行、金融、政企相关的项目中
风险点
启用灾备机房需要时间,数据不完整,业务会中断,而且启用后的服务不确定能否如期工作
解决方案
异地双活
伪异地双活
两个城市是通过跨城专线连通的
因两个机房距离较远,受物理距离的限制,现在两地之间的网络延迟变成了不可忽视的问题。北京到上海大约1300公里,即使架设一条高速网络专线,光纤以光速传输,一个来回也要近10ms的延迟,况且网络线路之间还会经历各种路由器、交换机等网络设备,实际延迟可能会达到30~100ms,如果网络发生抖动,延迟甚至会达到1s。不只是延迟,远距离的网络专线质量,也是远远达不到机房内网络质量的,专线网络经常会发生延迟、丢包、甚至中断的情况。这对业务影响很大,比如我们在app打开一个页面,可能会访问后端几十个API,每次都跨机房访问,整个页面的响应延迟可能就达到了秒级
风险点
延迟太大,影响业务和用户体验
解决方案
尽量避免跨机房调用,规避延迟问题
异地双活
现在两个机房的存储都是主库,而且两个机房的数据要互相同步数据,即客户端无论写哪一个机房,都会把这条数据同步到另一个机房
因两个机房都拥有了全量数据,故可支持任意切换机房,持续提供服务
上海机房的业务应用不再跨机房去读写北京机房的存储,只允许读写上海本地的存储,即就近访问,从而避免延迟问题
此时我们需要开发一些中间件来完成MySQL、Redis、MongoDB、MQ等数据的双向同步,以支持写入任意机房,数据都会同步至另一机房,从而两个机房的数据最终完全一致
业界也开源了很多数据同步的中间件,如阿里的Canal、RedisShake、MongoShake
此时即使专线出问题,我们的中间件可自动重试,直至成功,达到数据的最终一致
双活的重点是优先保证核心业务链路实现双活,而不是全部业务实现双活。实际上,有些服务如全局数据是无法做数据分片的,如系统配置、商品库存这类需强一致性的数据,这类服务依旧只能采用写主机房、读从机房的方案,不做双活
路由规则、路由转发、数据同步中间件、数据校验兜底策略,不仅需要开发强大的中间件,同时业务还要配合改造(业务边界划分、依赖拆分)等一系列工作,故异地双活需投入足够的人力物力
业务单元化划分、存储层数据双向同步、最上层的分片逻辑,这些是实现异地多活的关键、核心
风险点
双写问题:用户在很短的时间内修改了同一条数据,两个机房无法确认谁先谁后,数据发生冲突
解决方案
在数据同步中间件解决,中间件要有自动合并数据、解决冲突的能力,如以时间为标尺,以后到达的请求为准
这要求两个机房的时钟严格保持一致
这种依赖时钟解决冲突的方案并不严谨,故通常会采用下面第二种方案,从源头避免数据冲突的发生
从源头避免数据冲突
在最上层就把用户区分开,部分用户请求固定打到北京机房,其它用户请求固定打到上海机房。进入某个机房的用户请求,之后的所有业务操作都在这一个机房内完成,从根源上避免跨机房
需要在机房接入层之上再部署一个路由层,可配置路由规则,将用户分流到不同的机房
常见路由规则
按业务类型分片
将业务划分为不同的业务域,同一个业务域的应用部署到同一个机房内,另一个机房只作为热备。比如应用1、2只在北京机房接入流量,上海机房只是热备;应用3、4只在上海机房接入流量,北京机房只是热备。这样应用1、2的所有业务请求只读写北京机房存储,应用3、4的所有请求只读写上海机房存储
按业务类型在不同机房接入流量,需要考虑在多个应用之间的依赖关系,要尽可能把相关的业务应用部署在同一个机房,以避免跨机房调用。例如订单、支付服务有依赖关系,会产生互相调用,那这2个服务在A机房接入流量;社区、发帖服务有依赖关系,那这2个服务在B机房接入流量
直接哈希分片
在最上层的路由层,根据用户ID计算哈希取模,然后从路由表中找到对应的机房并把请求转发到该机房内
按地理位置分片
这种方案非常适合与地理位置密切相关的业务,例如打车、外卖服务
拿外卖服务举例,点外卖肯定是就近点餐,整个业务域相关的商家、用户、骑手,它们都是在相同的地理位置内的,即都在同一个机房内的,从而避免了跨机房数据冲突
分片的核心思路在于,让同一个用户的相关请求,只在一个机房内完成所有的业务闭环,不再出现跨机房访问
阿里称之为单元化
异地多活
异地多活只需在异地双活的基础上部署多个机房即可
这些服务按照单元化的部署方式,可让每个机房部署在任意地区,随时扩展新机房,只需在最上层定义好数据分片规则即可
随着扩展的机房越来越多,当一个机房写入数据后,需同步的机房会越来越多,整个方案复杂度会不断上升,为解决这一问题,业界将网状架构优化为星状架构
这种星状方案需设立一个中心机房,任意机房写入数据后都只同步到中心机房,再由中心机房同步至其它机房。这样一个机房写入数据后只需同步数据到中心机房即可,不需要再关心一共部署了多个机房,实现复杂度大大简化
与此同时,这个中心机房的稳定性要求会比较高,不过也还好,若中心机房发生故障,我们可以把任意一个机房提升为中心机房,继续按照之前的架构提供服务
多活的优势在于可以任意扩展机房就近部署。任意机房发生故障,可以快速完成切换,大大提高了系统的可用性。同时也不用担心系统规模的增长,因为这套架构具有极强的扩展能力
设计技巧
只保证核心业务的异地多活
只保证核心数据的最终一致性
采用多种手段同步数据
只保证绝大部分用户的异地多活
核心思想
采用多种手段,保证绝大部分用户的核心业务异地多活
实现步骤
业务分级
按照一定的标准将业务进行分级,挑选出核心的业务,只为核心业务设计异地多活,降低方案整体复杂度和实现的成本
常见的分级标准
访问量大的业务
核心业务
产生大量收入的业务
数据分类
挑选出核心业务后,需对核心业务相关的数据进一步分析,目的在于识别所有的数据及数据特征,这些数据特征会影响方案设计
常见的数据特征分析维度
数据量
数据量越大,同步延迟的几率越高
唯一性
如果数据要求必须唯一,要么只能一个中心点产生数据,要么需要设计一个数据唯一生成的算法
实时性
实时性要求越高,对同步的要求越高,方案越复杂
可丢失性
数据若可丢失,可降低方案的复杂度
登录过程中产生的session数据是可丢失的,用户只要重新登录就可生成新的session
可恢复性
如果数据可以恢复,说明对业务的影响不会那么大,可以相应地降低异地多活架构设计的复杂度
用户的微博丢失后,用户重新发一篇一模一样的微博,这个就是可恢复的;或者用户密码丢失,用户可以通过找回密码来重新设置一个新密码,这也算是可以恢复的
数据同步
异常处理
无论数据同步方案如何设计,一旦出现极端异常的情况,总是会有部分数据出现异常的。例如,同步延迟、数据丢失、数据不一致等
常见的异常处理措施
多通道同步
采取多种方式来进行数据同步,其中某条通道故障的情况下,系统可以通过其他方式来进行同步
同步和访问结合
由于有同步通道,优先读取本地数据,本地数据无法读取到再通过接口去访问远程机房数据,用于对实时性要求很高的场景
日志记录
主要用于故障恢复后对数据进行恢复,为每个关键操作都记录一条相关日志
用户补偿
无论多么完美的方案,故障场景下总可能有一小部分用户业务上出问题,我们可以采用人工的方式对用户进行补偿,弥补用户损失,培养用户的忠诚度
常见的补偿措施有送用户代金券、礼包、礼品、红包等
后记
系统发生故障并不可怕,最重要的是要有完善的监控体系,能及时发现异常,并以最快的速度恢复,才是高可用追求的目标
提升高可用的核心是冗余,备份、主从副本、同城灾备、同城双活、两地三中心、异地双活、异地多活都在做冗余
异地多活只能保证核心业务数据最终一致
数据要求强一致、不允许最终一致的业务不能做异地多活,如支付
对于要求强一致的服务,我们可通过跨机房的读写分离机制解决,即所有的写操作被路由到中心(Master)机房,而读操作可以在每个单元机房的Slave库执行,也可以bind到中心(Master)机房进行,这一切可基于数据库访问层(DAL)完成,从而实现业务基本无感知
应减少数据同步,只同步核心业务的关键数据
例如用户登录产生的token,量大,虽然数据丢失后需重新登录,但相比同步所有数据的代价,可以接受
采用多种手段同步数据
存储系统
MySQL/Redis等做双向同步
消息队列
通过消息队列同步数据
二次读取
若本地机房读取失败,则通过接口再读取其它机房数据
回源读取
根据路由判断源数据的位置,去读取
对于登录的session数据,由于数据量很大,我们可以不同步数据;当用户在A中心登录后,又在B中心登录,B中心拿到用户的session id后,根据路由判断session属于A中心,直接去A中心请求session数据即可;反之亦然,A中心也可以到B中心去获取session数据
重新生成数据
对于“回源读取”场景,如果异常情况下,A中心宕机了,B中心请求 session数据失败,此时就只能登录失败,让用户重新在B中心登录,生成新的session数据
要想实现异地多活,还需遵循一些原则,例如业务梳理、业务分级、数据分类、数据最终一致性保证、机房切换一致性保证、异常处理等。同时,相关的运维监控体系也要跟上
异地双/多活,最核心的包括:1. 业务单元化划分2. 存储层数据双向同步3. 最上层的分片逻辑/路由
跨国异地
主要是面向不同地区的用户提供服务,或者提供只读服务,对架构要求不高
亚马逊中国是为中国用户服务的,而亚马逊美国是为美国用户服务的,亚马逊中国的用户是无法用亚马逊中国的账号登录美国亚马逊的
混合云多活
异地机房可以是自建机房,也可以是云机房
混合云包括:1.云+自建IDC(私有云)2.云+其它厂商云
很多云厂商已提供混合云多活能力,如阿里云、华为云
多活切流时需要禁写保护以保证数据的一致性,故切流期间服务是不可用的,该过程可能持续数分钟
对于单元化的服务, 进入到单元以后的接入都需要尽量保证在本单元能够自行闭环
业界参考
饿了么
基本原则
业务内聚
单个订单的旅单过程,都在一个机房中完成,不允许跨机房调用。这个原则是为了保证实时性,旅单过程中不依赖另外一个机房的服务,才能保证没有延迟。我们称每个机房为一个 ezone,一个 ezone 包含了饿了么需要的各种服务。一笔业务能够内聚在一个 ezone 中,那么一个定单涉及的用户、商家、骑手,都会在相同的机房,恰好我们的业务是地域化的,通过合理的地域划分,也能够实现业务内聚
可用性优先
当发生故障切换机房时,优先保证系统可用,首先让用户可以下单吃饭,容忍有限时间段内的数据不一致,再事后修复。每个 ezone 都会有全量的业务数据,当一个 ezone 失效后,其他的 ezone 可以接管用户。用户在一个ezone的下单数据,会实时的复制到其他ezone
保证数据正确
在确保可用的情况下,需要对数据做保护以避免错误,在切换和故障时,如果发现某些订单的状态在两个机房不一致,会锁定该笔订单,阻止对它进行更改,保证数据的正确
业务可感
因为基础设施还没有强大到可以抹去跨机房的差异,需要让业务感知多活逻辑,业务代码要做一些改造,包括:需要业务代码能够识别出业务数据的归属,只处理本ezone的数据,过滤掉无关的数据。完善业务状态机,能够在数据出现不一致的时候,通过状态机发现和纠正
整体架构
APIRouter路由分发服务
API Router是一个HTTP反向代理和负载均衡器,部署在公有云中作为HTTP API流量的入口,它能识别出流量的归属 shard,并根据 shard 将流量转发到对应的 ezone。API Router 支持多种路由键,可以是地理位置,也可以是商户ID、订单ID等,最终由API Router映射为统一的 Sharding ID
SOA Proxy内部网关
SOA Proxy实现了对SOA调用的路由,执行和API Router 相似的逻辑,但只用在机房之间进行通信的场景。业务使用 SOA Proxy 需要对代码做一些修改,把路由信息加入到调用的上下文中
Global Zone Service全局状态协调器
GZS 维护着整个多活的路由表,其它所有的服务都从 GZS 订阅路由信息。切换机房的操作也在 GZS 控制台中完成。路由表包括:地理围栏信息,shard 到 ezone 的归属信息,商铺ID/订单ID 等路由逻辑层到 shard id 的映射关系等。GZS 通过在 SDK 端建立 Cache,来保证shard 逻辑能够最快速度执行,基本不需要和 GZS 交互,同时也有实时推送机制,确保在数据变更后能够快速通知到其他的服务
Data Replication Center数据复制
DRC 负责 Mysql 数据的实时双向复制,保证跨机房延时在 1s 以内。提供了基于时间的冲突解决方案,确保各个机房的数据一致。DRC除了复制数据,还对外提供了数据变更的通知,让业务能够感知到其他机房的数据变化,做相应的处理,例如清除Cache等。除了DRC,我们还有 ZK复制工具,RMQ 复制工具,Redis复制工具,基本每个数据层次,都有对应的复制方案
DRC有两个核心服务,Replicator和Apply,一个负责从源头数据库拉取变更,一个负责应用变更到目标数据库。类似Canal+Otter
Data Access Layer数据访问
数据访问层支撑了 Globa Zone 的逻辑,还提供了最后一道保护,拒绝路由错误的数据写入,是多活最底层的支撑
错误流量和切流期间禁写保护,避免脏写
华为云
多活监控
DCMonitor监控
DCMonitor用来监控多活机房状态,当机房发生宕机、脑裂等情况时,DCMonitor会在etcd中修改机房状态,并触发相应的脚本执行,比如脑裂时,在备用机房的SLB/F5中禁用业务的VIP
DBMonitor监控
DBMonitor用来监控本机房中数据层的状态,包括MySQL、SDS、DCS,当数据层变得不可用时,DBMonitor负责更新在etcd中状态,业务watch状态信息,及时切换数据操作
DCS/SDS/MySQL提供了SDK,在故障时,SDK能够感知并切换。如果业务没有使用相应SDK,可以使用confd监听etcd中的状态变更,更新本地的配置文件,业务中定期检查配置文件的更新,自己触发数据层读写的切换
切流管理
无状态切流
流量随机分发,不带地域属性,请求切到任意一个多活分区的处理都是一致的,对数据没有强制的依赖。这种切流相对简单,只需要调整DNS/GSLB的相关权重即可
强数据一致应用切流
数据分片,不同特征的数据流量必须要精准地在对应的分区进行处理
全局应用流量切流
针对全局应用, 数据采用单边读写,一旦出现单边故障优先实现数据的切流动作
接入层多活
DNS 随机路由分配
假设两个机房的地址为VIP1、VIP2,DNS中需要配置双VIP,可以使用GSLB配置对应的健康检查和智能切换,DNS 随机返回 VIP1/VIP2 或者VIP2/VIP1
客户端在访问服务端时:1、 先尝试第一个 VIP,如果成功,则使用它。2、 失败时,会自动尝试第二个 VIP;3、 如果第二个 VIP 也不能访问,则会抛出 IO 异常;4、 如果可访问,则后继访问一直使用它(每两次访问间隔不超过3分钟); 5、 如果超过 3 分钟无网络访问,当再次访问时,则重复步骤 1
终端SDK路由控制接入
终端在多活环境中路由控制有两种思路: 由DNS随机分配路由、云端分配路由
云端分配路由
云端分配路由需要增加TRS部件,先使用DNS随机分配路由的方式访问 TRS,由 TRS 返回路由信息,客户端先向TRS请求路由,得的归属机房的VIP,然后用这个VIP直接访问服务端
流量多活治理
流量多活治理主要采用Openresty系列的API Gateway来承载,通过订阅路由表信息,及客户的配置注册信息实现业务逻辑的策略转发和分流
API网关提供多种路由模式,包括策略路由和负载均衡路由策略路由: 通过判断 Header/Query/Path/源地址 实现不同参数取值访问不同目标 负载均衡策略: 支持加权轮询、加权最少链接、源地址哈希、URI哈希四种策略
数据层多活
DCS(Redis)
CSS(ElasticSearch)
读写分离方案通过ES原生插件的方式实现,实现单向同步需要客户主集群安装读写分离客户端插件,华为云CSS从集群使用支持读写分离特性的版本,并且打通主从集群间的9300端口网络。方案能实现将客户主集群的索引数据定时同步到华为云CSS集群,通过在从集群上调用相应的ES REST API即可完成全部配置