近年来业界频发大规模稳定性故障,让稳定性高可用这个历史话题又重新被业界所重视。对于爆发式增长的新业务,业务的发布迭代速度更快,相应的其稳定性挑战也更大。如何在支持业务高速增长的同时保障系统的稳定性?不断建设完善一个高可用的系统架构是我们的应对之道。
在 2024 年 6 月 14-15 日深圳 ArchSummit 架构师峰会 上,邀请了高德地图架构师邓学祥老师来演讲他们的技术经验,他会从大规模复杂业务的稳定性挑战具体问题出发,阐述大规模复杂业务的稳定性高可用整体技术方案,并详细介绍通过单元化来实现异地多活,提升系统的高可用等级到下一个 level 的详细技术方案;他还会介绍针对传统中心化网关一旦故障会造成整个业务不可用的问题,以及他们自研的 Service Mesh 基础设施来实现去中心化网关落地情况。
在会议召开前,我们采访了邓老师,让更多的人提前了解到邓老师的内容,以及他们团队在架构优化方面的探索与落地情况。
InfoQ:在演讲提纲中,你提到了处理大规模复杂业务的稳定性挑战,可以分享一些你在面对这些挑战时的具体案例和解决方案吗?
邓学祥:业务系统本身具有一定的复杂性:通常我们的系统都是基于各种中间件、基础库基础之上的,在这些基础之上开发功能效率高,工作量少,通常不太可能完全所有的代码都是自己开发的,通常不太可能基于 C 语言去实现业务系统。在享受基础库的效率优势的同时,也难免会引入稳定性风险,即使看起来很简单的一个功能点,如果我们算上基础库,中间件,低层代码,很简单的业务功能代码也会像一个大楼一样复杂。我们可能有不当使用中间件、基础库引发稳定性故障的情况,也有可能这些中间件、基础库本身有稳定性隐患,在某种场景下被触发了。
下游依赖导致的故障:通常我们的系统可能会依赖多个下游服务,这些下游服务可能是二方(公司或者部门内)的也可能是三方(公司外部)的服务。一般情况下我们不知道外部服务什么时候有变更或者有异常,依赖服务的故障有可能引起我们自身系统的稳定性。
因素故障:故障通常情况下是在有变更的时候触发的,变更时触发的故障通过回滚通常还比较好解决。但是在没有任何变更的情况下也有可能会出现故障,例如业务量的突然增长,系统容量不够带来的故障;例如单纯的随着时间变化,在某个时间点出现了故障,例如证书到期故障,例如 ID 增长超过 INT 最大值,而下游各个系统的字段值是 INT 型的故障。这些故障通常比较隐蔽一些,排查定位会比较耗时一些。
总结来说,大规模系统需要多方协作才能完成,但是多方协作也更容易带来稳定性风险。我们如何才能规避这些稳定性风险?通常需要我们做好以下几件事情:
监控是核心,只有有了监控,才能在有故障的情况下及时发现,及时处理解决掉。避免故障影响扩大。监控的核心是监控覆盖率全,这样才能有问题都能发现。但是监控覆盖全难度比较大,尤其是大规模复杂系统,通常需要通过链路梳理,链路上逐一加监控的方式才能尽可能的覆盖全。
预案降级也很关键,例如对于一些三方系统故障,如果我们提前就有预案降级,那么在出故障的情况下,快速启用预案来恢复就能很快的解决故障。此外重要的是,预案我们平时需要做演练来验证其正确性,负责在出问题的时候,可能不太敢真的去切换到预案。如果切换到没有验证过的预案后出错,有可能引发更大的故障。
对于重大风险的变更,千万不要一把梭,千万不要堵,而是要通过灰度的方式来实现变更,灰度会带来额外的工作量。但是对于重大风险,这个投入是值得的。
最后故障可能不能完全避免,出现了故障后,做好总结复盘,学费不能白交,通过反思总结改进,提升系统的稳定性水平,以后少出故障。
InfoQ:通过单元化来实现异地多活,可以详细介绍一下单元化的原理以及在实践中的具体应用场景吗?
邓学祥:单元化(Unitization or Set-based Architecture)是一种分布式系统设计模式,其核心理念是将原本单一的大规模系统划分为多个相对独立的单元,每个单元都包含各自独立封闭的完整服务,并能在物理或逻辑上独立运行。单元通常分布在不同区域,来实现异地多活。检验单元化水平最核心的指标是:是否是单元封闭。
实现单元化首先要实现单元化的路由规则,也就是如何划分单元的规则。通常有按业务数据来划分单元的,例如交易场景里按照买家或者卖家维度来划分单元。也有按照地域就近接入划分单元的,例如地图导航场景对 RT 要求高并且是无用户状态的。实现了路由规则以后,接下来还要实现服务的单元化部署和路由规则识别,还需要实现服务、中间件的单元化纠偏,最后在底层数据层的分区与同步,通常某一个单元的数据也是全量数据,以防止在其他单元异常情况下用户切换到这个单元时有用户数据。
单元化的应用场景方面,通常大促时,如果系统容量不够,在大促前会部署拉起多个单元,公共来抗流量峰值。单元化的另外一个应用场景通常是被用在异地容灾上面,例如某个城市的机房或者网络故障了,可以立即执行切换单元,业务能快速恢复不受影响。
InfoQ:提纲中提到了自研的 Go 中间件单元化技术方案,能否分享一些关于这个方案的具体实现细节和效果呢?
邓学祥:我们自研了阿里中间件的 Go 语言版本,在实现单元化过程中,中间件需要具备单元化纠偏能力,例如阿里的 RPC 框架 HSF,通常 HSF 调用下游服务时,优先选择本单元、本机房的下游服务,我们在 Go 版本里实现了单元化纠偏的能力,如果调用下游 HSF 服务时,发现根据路由规则是其他单元的则会路由调用其他单元的下游服务。
例如我们自研的 TDDL 分库分表中间件的 Go 语言版本,我们也实现了单元化需要的非本单元请求的禁写功能。只允许路由规则是本单元的数据写入本单元。防止本地更新数据和单元化同步更新同一条数据时的数据冲突导致数据错误,导致单元化数据同步链路故障。
InfoQ:演讲中会涉及 Service Mesh 基础设施的建设和业务落地方案,这种技术在提升系统稳定性和高可用性方面有哪些独特的优势?
邓学祥:我们的服务端有一个集中式的网关,所有客户端的请求都会先到网关经过验签、路由等步骤后再转发到后端业务服务。如果一旦网关故障,整个业务可能就不可用了,爆炸半径非常大。
利用 Service Mesh 技术,我们将网关能力通过 Sidecar 的方式下沉到各个后端服务上。不再经过中心式网关集群。各个服务的隔离性更好了,小的边缘服务故障不好影响主业务功能的使用。极大的提升了系统的稳定性水平。接触了集中式网关爆炸半径过大的隐患。
InfoQ:面对爆发式增长的业务,你们团队如何平衡高可用与单元化成本之间的关系?是否可以分享一些您的经验和策略?
邓学祥:的确实现单元化服务是需要额外成本的,开发成本少不了,毕竟需要开发才能具备完整的单元化能力,但是单元化的服务范围可以控制,我们只做了最核心链路的单元化,其他非核心链路就没有必要实现单元化,减少开发测试工作量。
单元化比较大的成本是服务器和存储资源的成本。首先各个单元都需要数据库,而且每个单元的数据库都是全量数据,数据库成本和数据同步链路的成本都不低。其次服务部署多单元,如果服务容量打的不是特别满,也会带来额外的服务器成本。我们的平衡策略是在平时只部署两个单元,减少单元化成本,在大促时扩到多个单元,多个单元一起来抗业务峰值流量。这样在实现单元化功能的同时也能最大限度的减小单元化成本。
InfoQ:在处理大规模复杂业务的过程中,您遇到的一些高级坑是什么?可以分享一些典型案例以及如何规避这些坑的经验吗?
邓学祥:分享一个比较高级的坑,有一次实现一个业务需求我们和以前一样变更数据库表结构,将一个字段类型从 TINYINT 变更为 INT 类型。以前执行过类似的变更没有什么问题。但是在我们实现了单元化之后执行这个数据库变更,导致了锁表引发线上故障。原因是我们的基础设施对非单元化数据库的字段类型变更实现了无锁变更。而单元化数据库由于可能有数据变更冲突,采用的是 MySQL 有锁变更的方式,导致锁表故障。更详细的原理情况如果大家有兴趣可以听我后面在大会上的分享。
如何避坑?一方面对开发同学加强技术原理的分享,在大家都熟知技术原理的情况下,可能就会少踩坑。另一方面借鉴他人经验是个很好的办法,其他人踩过的坑,基本上都是比较容易犯错误的坑,我们也可能会遇到,借鉴他们的经验,我们就不要再踩同样的坑了。
InfoQ:你认为异地多活技术架构在应对业务高速增长的同时保障系统稳定性方面有哪些挑战?你们又是如何应对这些挑战的?
邓学祥:就我们的业务而言,我们主要遇到过以下几个挑战:
多单元之间的数据实时同步是一大挑战,交易业务是强事务型业务,而且业务复杂,任何短暂的数据不一致都可能导致业务逻辑错误或者数据错乱,甚至数据丢失。我们的应对方法:自研的 Go 语言版 TDDL 分布式数据库中间件的单元禁写策略来保障不会造成数据冲突、数据错乱。业务上采用最终一致性策略来确保数据在各个数据中心间的一致性。
单元故障的快速识别、监控和处理,及时将用户切换到健康的单元,需要完善的监控预警和自动化容灾切换机制。我们的应对方法:建立全面的单元化监控系统,实现实时健康检查,实时检测单元间的数据同步延迟,延迟过大自动报警。确保在单元故障时能快速感知并报警。建设统一的单元化管控平台,一键完成单元化切换。未来在继续迭代优化,监控准确无误判的情况下再实现自动单元化故障检测和单元化自动切换。
单元化环境下的运维工作相比以往更为复杂,包括版本升级、数据迁移、扩容缩容等日常运维任务,也包括需求迭代过程中的数据库变更等(例如我前面提到的单元化数据库变更踩坑示例)。我们的应对方法:利用集团的标准化、自动化运维工具和流程,严格按照变更规范操作,确保运维操作的安全稳定。
InfoQ:在自研 Service Mesh 基础设施方面,您是如何保证其在大规模复杂业务中的有效落地的?是否可以分享一些实践经验?
邓学祥:Service Mesh 落地过程我们比较谨慎,毕竟是涉及网关入口这么重要系统的改造。稳定性是落地过程中最重要的考虑因素,初版研发完成后,首先开发同学进行了各种场景的模拟测试,包含破坏性测试,混沌工程的故障注入测试,确保我们 Service Mesh 中间件本身的健壮性和稳定性。
落地过程中,我们首先选择不重要的小流量业务场景来灰度试验落地。在实际线上流量中检测完善我们的 Service Mesh 中间件基础设施。在小流量小业务中运行无问题后,再推广到大流量小业务(非核心低影响功能)中来继续试点,经受住大流量的性能优化。优化完成后再继续在其他业务中推广。业务推广过程中,也遵循可监控、可灰度、可回滚的原则,逐步将流量从中心化网关往 Service Mesh 灰度迁移。遇到问题则回滚优化完成后,继续灰度放量,直到最终放量完成。
InfoQ:在面对新技术应用时,您是如何平衡收益和风险的?可以分享一些控制风险的具体方法和策略吗?
邓学祥:新技术应用永远都是风险与收益并存,在享受新技术带来的收益时,也不可避免的要承担新技术应用的风险,这个风险来自两个部分,一方面是新技术本身成熟需要有一个过程,需要第一个吃螃蟹的来不断实践优化。另一方面对于团队来说首次应用新技术也面临经验不足、使用姿势不当等风险。核心还是前面说的通过可监控、可灰度、可回滚的策略摸着石头过河,逐步完成新技术落地,安全无故障的享受新技术落地后的收益。
活动推荐
本届 ArchSummit 会议上,我们邀请了 CNCF、顺丰集团、阿里、腾讯、百度等企业的专家来演讲。会议上还设置了大模型应用、架构升级、智算平台、AI 编程、成本优化等专题和话题内容。如您感兴趣,可点击「阅读原文」查看更多详情。目前会议进入 9 折购票阶段,可以联系票务经理 17310043226 , 锁定最新优惠。
继续阅读
阅读原文