从“挖光缆”到“剪网线”|蚂蚁金服异地多活单元化架构下的微服务体系

本文涉及的产品
云原生网关 MSE Higress,422元/月
任务调度 XXL-JOB 版免费试用,400 元额度,开发版规格
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
简介: “异地多活”是互联网系统的一种高可用部署架构,而“单元化”正是实现异地多活的一个解题思路。 说起这个话题,不得不提两个事件:一件是三年多前的往事,另一件就发生今年的杭州云栖大会上。

“异地多活”是互联网系统的一种高可用部署架构,而“单元化”正是实现异地多活的一个解题思路。说起这个话题,不得不提两个事件:一件是三年多前的往事,另一件就发生今年的杭州云栖大会上。

从“挖光缆”到“剪网线”

2015年5月27日,因市政施工,支付宝杭州某数据中心的光缆被挖断,造成对部分用户服务不可用,时间长达数小时。其实支付宝的单元化架构容灾很早就开始启动了,2015年也基本上成型了。当时由于事发突然,还是碰到很多实际问题,花费了数小时的时间,才在确保用户数据完全正确的前提下,完成切换、恢复服务。虽然数据没有出错,但对于这样体量的公司来说,服务不可用的社会舆论影响也是非常大的。

527这个数字,成为蚂蚁金服全体技术人心中悬着那颗苦胆。我们甚至把技术部门所在办公楼的一个会议室命名为527,把每年的5月27日定为技术日,来时刻警醒自己敬畏技术,不断打磨技术。
image1.png | center | 1920x1080
经过几年的卧薪尝胆,时间来到2018年9月。云栖大会上,蚂蚁金服发布了“三地五中心金融级高可用方案”。现场部署了一个模拟转账系统,在场观众通过小程序互相不断转账。服务端分布在三个城市的五个数据中心,为了感受更直观,把杭州其中一个数据中心机柜设置在了会场。工作人员当场把杭州两个数据中心的网线剪断,来模拟杭州的城市级灾难。
image2.png | center | 1920x1080
网线剪断之后,部分用户服务不可用。经过26秒,容灾切换完成,所有受影响的用户全部恢复正常。这个Demo当然只是实际生产系统的一个简化模型,但是其背后的技术是一致的。这几年来,其实每隔几周我们就会在生产环境做一次真实的数据中心断网演习,来不断打磨系统容灾能力。

从大屏幕上可以看到,容灾切换包含了“数据库切换”“缓存容灾切换”“多活规则切换”“中间件切换”“负载均衡切换”“域名解析切换”等多个环节。异地多活架构是一个复杂的系统工程,其包含的技术内涵非常丰富,单场分享实难面面俱到。本场是微服务话题专场,我们也将以应用层的微服务体系作为切入点,一窥异地多活单元化架构的真面目。

去单点之路

任何一个互联网系统发展到一定规模时,都会不可避免地触及到单点瓶颈。“单点”在系统的不同发展阶段有不同的表现形式。提高系统伸缩能力和高可用能力的过程,就是不断与各种层面的单点斗争的过程。
image3.png | center | 1920x1080
我们不妨以一个生活中最熟悉的场景作为贯穿始终的例子,来推演系统架构从简单到复杂,所遇到的问题。
image4.png | center | 1920x1080
上图展示的是用支付宝买早餐的情景,当然角色是虚构的。
最早支付宝只是从淘宝剥离的一个小工具系统,处于单体应用时代。这个时候移动支付当然还没出现,我们的例子仅用于帮助分析问题,请忽略这个穿帮漏洞。
image5.png | center | 1920x1080
假设图中的场景发生在北京,而支付宝系统是部署在杭州的机房。在小王按下“支付”按钮的一瞬间,会发生什么事情呢?
支付请求要从客户端发送到服务端,服务端最终再把结果返回客户端,必然会有一次异地网络往返,耗时大约在数十毫秒的数量级,我们用红色线表示。应用进程内部会发生很多次业务逻辑运算,用绿色圈表示,不涉及网络开销,耗时忽略不计。应用会访问多次数据库,由于都在部署在同一个机房内,每次耗时按一毫秒以下,一笔支付请求按10次数据库访问算(对于支付系统来说并不算多,一笔业务可能涉及到各种数据校验、数据修改)。耗时大头在无可避免的用户到机房物理距离上,系统内部处理耗时很小。
image6.png | center | 1920x1080
到了服务化时代,一个好的RPC框架追求的是让远程服务调用像调本地方法一样简单。随着服务的拆分、业务的发展,原本进程内部的调用变成了网络调用。由于应用都部署在同一个机房内,业务整体网络耗时仍然在可接受范围内。开发人员一般也不会特别在意这个问题,RPC服务被当成几乎无开销成本地使用,应用的数量也在逐渐膨胀。

服务化解决了应用层的瓶颈,紧接着数据库就成为制约系统扩展的瓶颈。虽然我们本次重点讨论的是服务层,但要讲单元化,数据存储是无论如何绕不开的话题。这里先插播一下分库分表的介绍,作为一个铺垫。
image7.png | center | 1920x1080
通过引入数据访问中间件,可以实现对应用透明的分库分表。一个比较好的实践是:逻辑拆分先一步到位,物理拆分慢慢进行。以账户表为例,将用户ID的末两位作为分片维度,可以在逻辑上将数据分成100份,一次性拆到100个分表中。这100个分表可以先位于同一个物理库中,随着系统的发展,逐步拆成2个、5个、10个,乃至100个物理库。数据访问中间件会屏蔽表与库的映射关系,应用层不必感知。

解决了应用层和数据库层单点后,物理机房又成为制约系统伸缩能力和高可用能力的最大单点。
image8.png | center | 1920x1080
要突破单机房的容量限制,最直观的解决办法就是再建新的机房,机房之间通过专线连成同一个内部网络。应用可以部署一部分节点到第二个机房,数据库也可以将主备库交叉部署到不同的机房。
这一阶段,只是解决了机房容量不足的问题,两个机房逻辑上仍是一个整体。日常会存在两部分跨机房调用:

  1. 服务层逻辑上是无差别的应用节点,每一次RPC调用都有一半的概率跨机房
  2. 每个特定的数据库主库只能位于一个机房,所以宏观上也一定有一半的数据库访问是跨机房的
    同城跨机房专线访问的耗时在数毫秒级,图中用黄色线表示。随着微服务化演进如火如荼,这部分耗时积少成多也很可观。

image9.png | center | 1920x1080
改进后的同城多机房架构,依靠不同服务注册中心,将应用层逻辑隔离开。只要一笔请求进入一个机房,应用层就一定会在一个机房内处理完。当然,由于数据库主库只在其中一边,所以这个架构仍然不解决一半数据访问跨机房的问题。
这个架构下,只要在入口处调节进入两个机房的请求比例,就可以精确控制两个机房的负载比例。基于这个能力,可以实现全站蓝绿发布。
image10.png | center | 1920x1080
“两地三中心”是一种在金融系统中广泛应用的跨数据中心扩展与跨地区容灾部署模式,但也存在一些问题。异地灾备机房距离数据库主节点距离过远、访问耗时过长,异地备节点数据又不是强一致的,所以无法直接提供在线服务。
在扩展能力上,由于跨地区的备份中心不承载核心业务,不能解决核心业务跨地区扩展的问题;在成本上,灾备系统仅在容灾时使用,资源利用率低,成本较高;在容灾能力上,由于灾备系统冷备等待,容灾时可用性低,切换风险较大。

小结一下前述几种架构的特点。直到这时,微服务体系本身的变化并不大,无非是部署几套、如何隔离的问题,每套微服务内部仍然是简单的架构。

架构类型 优势 问题
单体应用 网络开销小 扩展性差,维护困难
单机房服务化 解耦,可扩展 容量受限,机房级单点
同城多机房阶段一 突破单机房容量瓶颈 非必要的跨机房网络开销大
同城多机房阶段二 非必要的跨机房网络开销小;机房级容灾能力 城市级单点
两地三中心 异地容灾能力 网络耗时与数据一致性难两全

蚂蚁金服单元化实践

蚂蚁金服发展单元化架构的原始驱动力,可以概括为两句话:

  • 异地多活容灾需求带来的数据访问耗时问题,量变引起质变
  • 数据库连接数瓶颈制约了整体水平扩展能力,危急存亡之秋

第一条容易理解,正是前面讨论的问题,传统的两地三中心架构在解决地区级单点问题上效果并不理想,需要有其他思路。但这毕竟也不是很急的事情,真正把单元化之路提到生死攸关的重要性的,是第二条。

到2013年,支付宝核心数据库都已经完成了水平拆分,容量绰绰有余,应用层无状态,也可以随意水平扩展。但是按照当年双十一的业务指标做技术规划的时候,却碰到了一个棘手的问题:Oracle数据库的连接不够用了。
image11.png | center | 1920x1080
虽然数据库是按用户维度水平拆分的,但是应用层流量是完全随机的。以图中的简化业务链路为例,任意一个核心应用节点C可能访问任意一个数据库节点D,都需要占用数据库连接。连接是数据库非常宝贵的资源,是有上限的。当时的支付宝,面临的问题是不能再对应用集群扩容,因为每增加一台机器,就需要在每个数据分库上新增若干连接,而此时几个核心数据库的连接数已经到达上限。应用不能扩容,意味着支付宝系统的容量定格了,不能再有任何业务量增长。别说大促,可能再过一段时间连日常业务也支撑不了了。
image12.png | center | 1920x1080
单元化架构基于这样一种设想:如果应用层也能按照数据层相同的拆片维度,把整个请求链路收敛在一组服务器中,从应用层到数据层就可以组成一个封闭的单元。数据库只需要承载本单元的应用节点的请求,大大节省了连接数。“单元”可以作为一个相对独立整体来挪动,甚至可以把部分单元部署到异地去。

单元化有几个重要的设计原则:

  • 核心业务必须是可分片的
  • 必须保证核心业务的分片是均衡的,比如支付宝用用户ID作分片维度
  • 核心业务要尽量自包含,调用要尽量封闭
  • 整个系统都要面向逻辑分区设计,而不是物理部署

在实践上,我们推荐先从逻辑上切分若干均等的单元,再根据实际物理条件,把单元分布到物理数据中心。单元均等的好处是更容易做容量规划,可以根据一个单元的压测结果方便换算成整站容量。

我们把单元叫做Regional Zone。例如,数据按100份分片,逻辑上分为5个Regional Zone,每个承载20份数据分片的业务。初期可能是部署成两地三中心(允许多个单元位于同一个数据中心)。随着架构的发展,再整单元搬迁,演化成三地五中心,应用层无需感知物理层面的变化。
image13.png | center | 1920x1080
image14.png | center | 1920x1080
回到前面买早餐的例子,小王的ID是12345666,分片号是66,应该属于Regional Zone 04;而张大妈ID是54321233,分片号33,应该属于Regional Zone 02。
image15.png | center | 1920x1080
应用层会自动识别业务参数上的分片位,将请求发到正确的单元。业务设计上,我们会保证流水号的分片位跟付款用户的分片位保持一致,所以绝大部分微服务调用都会收敛在Regional Zone 04内部。
但是转账操作一定会涉及到两个账户,很可能位于不同的单元。张大妈的账号就刚好位于另一个城市的Regional Zone 02。当支付系统调用账务系统给张大妈的账号加钱的时候,就必须跨单元调用Regional Zone 02的账务服务。图中用红线表示耗时很长(几十毫秒级)的异地访问。
image16.png | center | 1920x1080
从宏观耗时示意图上就可以比较容易地理解单元化的思想了:单元内高内聚,单元间低耦合,跨单元调用无法避免,但应该尽量限定在少数的服务层调用,把整体耗时控制在可接受的范围内(包括对直接用户体验和对整体吞吐量的影响)。

前面讲的是正常情况下如何“多活”,机房故障情况下就要发挥单元之间的容灾互备作用了。
image17.png | center | 1920x1080
一个城市整体故障的情况下,应用层流量通过规则的切换,由事先规划好的其他单元接管。

数据层则是依靠自研的基于Paxos协议的分布式数据库OceanBase,自动把对应容灾单元的从节点选举为主节点,实现应用分片和数据分片继续收敛在同一单元的效果。我们之所以规划为“两地三中心”“三地五中心”这样的物理架构,实际上也是跟OceanBase的副本分布策略息息相关的。数据层异地多活,又是另一个宏大的课题了,以后可以专题分享,这里只简略提过。

这样,借助单元化异地多活架构,才能实现开头展示的“26秒完成城市级容灾切换”能力。

关键技术组件

单元化是个复杂的系统工程,需要多个组件协同工作,从上到下涉及到DNS层、反向代理层、网关/WEB层、服务层、数据访问层。
总体指导思想是“多层防线,迷途知返”。每层只要能获取到足够的信息,就尽早将请求转到正确的单元去,如果实在拿不到足够的信息,就靠下一层。
image18.png | center | 1920x1080

  • DNS层照理说感知不到任何业务层的信息,但我们做了一个优化叫“多域名技术”。比如PC端收银台的域名是cashier.alipay.com,在系统已知一个用户数据属于哪个单元的情况下,就让其直接访问一个单独的域名,直接解析到对应的数据中心,避免了下层的跨机房转发。例如上图中的cashiergtj.alipay.com,gtj就是内部一个数据中心的编号。移动端也可以靠下发规则到客户端来实现类似的效果。
  • 反向代理层是基于Nginx二次开发的,后端系统在通过参数识别用户所属的单元之后,在Cookie中写入特定的标识。下次请求,反向代理层就可以识别,直接转发到对应的单元。
  • 网关/Web层是应用上的第一道防线,是真正可以有业务逻辑的地方。在通用的HTTP拦截器中识别Session中的用户ID字段,如果不是本单元的请求,就 forward到正确的单元。并在Cookie中写入标识,下次请求在反向代理层就可以正确转发。
  • 服务层RPC框架和注册中心内置了对单元化能力的支持,可以根据请求参数,透明地找到正确单元的服务提供方。
  • 数据访问层是最后的兜底保障,即使前面所有的防线都失败了,一笔请求进入了错误的单元,在访问数据库的时候也一定会去正确的库表,最多耗时变长,但绝对不会访问到错误的数据。

image19.png | center | 1920x1080
这么多的组件要协同工作,必须共享同一份规则配置信息。必须有一个全局的单元化规则管控中心来管理,并通过一个高效的配置中心下发到分布式环境中的所有节点。
规则的内容比较丰富,描述了城市、机房、逻辑单元的拓扑结构,更重要的是描述了分片ID与逻辑单元之间的映射关系。

image20.png | center | 1920x1080
服务注册中心内置了单元字段,所有的服务提供者节点都带有“逻辑单元”属性。不同机房的注册中心之间互相同步数据,最终所有服务消费者都知道每个逻辑单元的服务提供者有哪些。RPC框架就可以根据需要选择调用目标。
image21.png | center | 1920x1080
image22.png | center | 1920x1080
RPC框架本身是不理解业务逻辑的,要想知道应该调哪个单元的服务,信息只能从业务参数中来。如果是从头设计的框架,可能直接约定某个固定的参数代表分片ID,要求调用者必须传这个参数。但是单元化是在业务已经跑了好多年的情况下的架构改造,不可能让所有存量服务修改接口。要求调用者在调用远程服务之前把分片ID放到ThreadLocal中?这样也很不优雅,违背了RPC框架的透明原则。
于是我们的解决方案是框架定义一个接口,由服务提供方给出一个实现类,描述如何从业务参数中获取分片ID。服务提供方在接口上打注解,告诉框架实现类的路径。框架就可以在执行RPC调用的时候,根据注解的实现,从参数中截出分片ID。再结合全局路由规则中分片ID与逻辑单元之间的映射关系,就知道该选择哪个单元的服务提供方了。

后记

本文着重介绍了蚂蚁金服异地多活单元化架构的原理,以及微服务体系在此架构下的关键技术实现。要在工程层面真正落地单元化,涉及的技术问题远不止此。例如:数据层如何容灾?无法水平拆分的业务如何处理?

蚂蚁技术团队会坚持走技术开放路线,后续还会以不同的形式分享相关话题,也欢迎各位读者留言探讨。

image | left | 216x216

长按关注,获取分布式架构干货

欢迎大家共同打造 SOFAStack https://github.com/alipay

目录
打赏
0
3
2
0
78493
分享
相关文章
云原生时代的应用架构演进:从微服务到 Serverless 的阿里云实践
云原生技术正重塑企业数字化转型路径。阿里云作为亚太领先云服务商,提供完整云原生产品矩阵:容器服务ACK优化启动速度与镜像分发效率;MSE微服务引擎保障高可用性;ASM服务网格降低资源消耗;函数计算FC突破冷启动瓶颈;SAE重新定义PaaS边界;PolarDB数据库实现存储计算分离;DataWorks简化数据湖构建;Flink实时计算助力风控系统。这些技术已在多行业落地,推动效率提升与商业模式创新,助力企业在数字化浪潮中占据先机。
86 12
智慧工地云平台的技术架构解析:微服务+Spring Cloud如何支撑海量数据?
慧工地解决方案依托AI、物联网和BIM技术,实现对施工现场的全方位、立体化管理。通过规范施工、减少安全隐患、节省人力、降低运营成本,提升工地管理的安全性、效率和精益度。该方案适用于大型建筑、基础设施、房地产开发等场景,具备微服务架构、大数据与AI分析、物联网设备联网、多端协同等创新点,推动建筑行业向数字化、智能化转型。未来将融合5G、区块链等技术,助力智慧城市建设。
110 0
微服务引擎 MSE:打造通用的企业级微服务架构
微服务引擎MSE致力于打造通用的企业级微服务架构,涵盖四大核心内容:微服务技术趋势与挑战、MSE应对方案、拥抱开源及最佳实践。MSE通过流量入口、内部流量管理、服务治理等模块,提供高可用、跨语言支持和性能优化。此外,MSE坚持开放,推动云原生与AI融合,助力企业实现无缝迁移和高效运维。
130 1
微服务架构解析:跨越传统架构的技术革命
微服务架构(Microservices Architecture)是一种软件架构风格,它将一个大型的单体应用拆分为多个小而独立的服务,每个服务都可以独立开发、部署和扩展。
1178 36
微服务架构解析:跨越传统架构的技术革命
云原生架构下的微服务治理策略与实践####
本文旨在探讨云原生环境下微服务架构的治理策略,通过分析当前面临的挑战,提出一系列实用的解决方案。我们将深入讨论如何利用容器化、服务网格(Service Mesh)等先进技术手段,提升微服务系统的可管理性、可扩展性和容错能力。此外,还将分享一些来自一线项目的经验教训,帮助读者更好地理解和应用这些理论到实际工作中去。 ####
96 0
从单体到微服务:如何借助 Spring Cloud 实现架构转型
**Spring Cloud** 是一套基于 Spring 框架的**微服务架构解决方案**,它提供了一系列的工具和组件,帮助开发者快速构建分布式系统,尤其是微服务架构。
417 69
从单体到微服务:如何借助 Spring Cloud 实现架构转型
后端服务架构的微服务化转型
本文旨在探讨后端服务从单体架构向微服务架构转型的过程,分析微服务架构的优势和面临的挑战。文章首先介绍单体架构的局限性,然后详细阐述微服务架构的核心概念及其在现代软件开发中的应用。通过对比两种架构,指出微服务化转型的必要性和实施策略。最后,讨论了微服务架构实施过程中可能遇到的问题及解决方案。
探索微服务架构下的API网关设计
在微服务的大潮中,API网关如同一座桥梁,连接着服务的提供者与消费者。本文将深入探讨API网关的核心功能、设计原则及实现策略,旨在为读者揭示如何构建一个高效、可靠的API网关。通过分析API网关在微服务架构中的作用和挑战,我们将了解到,一个优秀的API网关不仅要处理服务路由、负载均衡、认证授权等基础问题,还需考虑如何提升系统的可扩展性、安全性和可维护性。文章最后将提供实用的代码示例,帮助读者更好地理解和应用API网关的设计概念。
131 8
后端架构演进:从单体到微服务####
本文将探讨后端架构的演变过程,重点分析从传统的单体架构向现代微服务架构的转变。通过实际案例和理论解析,揭示这一转变背后的技术驱动力、挑战及最佳实践。文章还将讨论在采用微服务架构时需考虑的关键因素,包括服务划分、通信机制、数据管理以及部署策略,旨在为读者提供一个全面的架构转型视角。 ####
92 1
深入解析微服务架构中的服务发现与负载均衡
深入解析微服务架构中的服务发现与负载均衡
286 7