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

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

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

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

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

相关文章
|
17天前
|
缓存 负载均衡 JavaScript
探索微服务架构下的API网关模式
【10月更文挑战第37天】在微服务架构的海洋中,API网关犹如一座灯塔,指引着服务的航向。它不仅是客户端请求的集散地,更是后端微服务的守门人。本文将深入探讨API网关的设计哲学、核心功能以及它在微服务生态中扮演的角色,同时通过实际代码示例,揭示如何实现一个高效、可靠的API网关。
|
15天前
|
Cloud Native 安全 数据安全/隐私保护
云原生架构下的微服务治理与挑战####
随着云计算技术的飞速发展,云原生架构以其高效、灵活、可扩展的特性成为现代企业IT架构的首选。本文聚焦于云原生环境下的微服务治理问题,探讨其在促进业务敏捷性的同时所面临的挑战及应对策略。通过分析微服务拆分、服务间通信、故障隔离与恢复等关键环节,本文旨在为读者提供一个关于如何在云原生环境中有效实施微服务治理的全面视角,助力企业在数字化转型的道路上稳健前行。 ####
|
20天前
|
设计模式 Java API
微服务架构演变与架构设计深度解析
【11月更文挑战第14天】在当今的IT行业中,微服务架构已经成为构建大型、复杂系统的重要范式。本文将从微服务架构的背景、业务场景、功能点、底层原理、实战、设计模式等多个方面进行深度解析,并结合京东电商的案例,探讨微服务架构在实际应用中的实施与效果。
78 6
|
20天前
|
设计模式 Java API
微服务架构演变与架构设计深度解析
【11月更文挑战第14天】在当今的IT行业中,微服务架构已经成为构建大型、复杂系统的重要范式。本文将从微服务架构的背景、业务场景、功能点、底层原理、实战、设计模式等多个方面进行深度解析,并结合京东电商的案例,探讨微服务架构在实际应用中的实施与效果。
31 1
|
16天前
|
Dubbo Java 应用服务中间件
服务架构的演进:从单体到微服务的探索之旅
随着企业业务的不断拓展和复杂度的提升,对软件系统架构的要求也日益严苛。传统的架构模式在应对现代业务场景时逐渐暴露出诸多局限性,于是服务架构开启了持续演变之路。从单体架构的简易便捷,到分布式架构的模块化解耦,再到微服务架构的精细化管理,企业对技术的选择变得至关重要,尤其是 Spring Cloud 和 Dubbo 等微服务技术的对比和应用,直接影响着项目的成败。 本篇文章会从服务架构的演进开始分析,探索从单体项目到微服务项目的演变过程。然后也会对目前常见的微服务技术进行对比,找到目前市面上所常用的技术给大家进行讲解。
35 1
服务架构的演进:从单体到微服务的探索之旅
|
14天前
|
消息中间件 监控 安全
后端架构演进:从单体到微服务####
在数字化转型的浪潮中,企业应用的后端架构经历了从传统单体架构到现代微服务架构的深刻变革。本文探讨了这一演进过程的背景、驱动力、关键技术及面临的挑战,揭示了如何通过微服务化实现系统的高可用性、扩展性和敏捷开发,同时指出了转型过程中需克服的服务拆分、数据管理、通信机制等难题,为读者提供了一个全面理解后端架构演变路径的视角。 ####
37 8
|
15天前
|
Cloud Native 安全 API
云原生架构下的微服务治理策略与实践####
—透过云原生的棱镜,探索微服务架构下的挑战与应对之道 本文旨在探讨云原生环境下,微服务架构所面临的关键挑战及有效的治理策略。随着云计算技术的深入发展,越来越多的企业选择采用云原生架构来构建和部署其应用程序,以期获得更高的灵活性、可扩展性和效率。然而,微服务架构的复杂性也带来了服务发现、负载均衡、故障恢复等一系列治理难题。本文将深入分析这些问题,并提出一套基于云原生技术栈的微服务治理框架,包括服务网格的应用、API网关的集成、以及动态配置管理等关键方面,旨在为企业实现高效、稳定的微服务架构提供参考路径。 ####
42 5
|
17天前
|
监控 API 微服务
后端技术演进:从单体架构到微服务的转变
随着互联网应用的快速增长和用户需求的不断演化,传统单体架构已难以满足现代软件开发的需求。本文深入探讨了后端技术在面对复杂系统挑战时的演进路径,重点分析了从单体架构向微服务架构转变的过程、原因及优势。通过对比分析,揭示了微服务架构如何提高系统的可扩展性、灵活性和维护效率,同时指出了实施微服务时面临的挑战和最佳实践。
42 7
|
16天前
|
Kubernetes 负载均衡 Cloud Native
云原生架构下的微服务治理策略
随着云原生技术的不断成熟,微服务架构已成为现代应用开发的主流选择。本文探讨了在云原生环境下实施微服务治理的策略和方法,重点分析了服务发现、负载均衡、故障恢复和配置管理等关键技术点,以及如何利用Kubernetes等容器编排工具来优化微服务的部署和管理。文章旨在为开发者提供一套实用的微服务治理框架,帮助其在复杂的云环境中构建高效、可靠的分布式系统。
32 5
|
16天前
|
负载均衡 监控 Cloud Native
云原生架构下的微服务治理策略与实践####
在数字化转型浪潮中,企业纷纷拥抱云计算,而云原生架构作为其核心技术支撑,正引领着一场深刻的技术变革。本文聚焦于云原生环境下微服务架构的治理策略与实践,探讨如何通过精细化的服务管理、动态的流量调度、高效的故障恢复机制以及持续的监控优化,构建弹性、可靠且易于维护的分布式系统。我们将深入剖析微服务治理的核心要素,结合具体案例,揭示其在提升系统稳定性、扩展性和敏捷性方面的关键作用,为读者提供一套切实可行的云原生微服务治理指南。 ####