微信号:infoqchina

介绍:有内容的技术社区媒体

从“被动挖光缆”到“主动剪网线”,蚂蚁金服异地多活的微服务体系

2018-11-29 08:30 玄霄
作者 | 玄霄
出处 | 金融级分布式架构公众号
蚂蚁金服(当时还是支付宝)从 2013 年起就运行在单元化架构上,除了具备异地容灾能力外,还能做到异地多活,可随时在多城市、多数据中心调配流量。基于单元流量调配机制,可实现大规模集群的蓝绿发布、灰度仿真环境,为充分验证业务正确性、降低故障提供了基础条件。相应地,微服务体系也必须具备单元内收敛、单元间可控路由等能力,来支撑单元化技术架构的落地。本文根据玄霄 2018 年上海 QCon 演讲内容整理。

“异地多活”是互联网系统的一种高可用部署架构,而“单元化”正是实现异地多活的一个解题思路。

说起这个话题,不得不提两个事件:一件是三年多前的往事,另一件就发生今年的杭州云栖大会上。

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

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

527 这个数字,成为蚂蚁金服全体技术人心中悬着那颗苦胆。我们甚至把技术部门所在办公楼的一个会议室命名为 527,把每年的 5 月 27 日定为技术日,来时刻警醒自己敬畏技术,不断打磨技术。

经过几年的卧薪尝胆,时间来到 2018 年 9 月。云栖大会上,蚂蚁金服发布了“三地五中心金融级高可用方案”。现场部署了一个模拟转账系统,在场观众通过小程序互相不断转账。服务端分布在三个城市的五个数据中心,为了感受更直观,把杭州其中一个数据中心机柜设置在了会场。工作人员当场把杭州两个数据中心的网线剪断,来模拟杭州的城市级灾难。

网线剪断之后,部分用户服务不可用。经过 26 秒,容灾切换完成,所有受影响的用户全部恢复正常。这个 Demo 当然只是实际生产系统的一个简化模型,但是其背后的技术是一致的。这几年来,其实每隔几周我们就会在生产环境做一次真实的数据中心断网演习,来不断打磨系统容灾能力。

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

去单点之路

任何一个互联网系统发展到一定规模时,都会不可避免地触及到单点瓶颈。“单点”在系统的不同发展阶段有不同的表现形式。提高系统伸缩能力和高可用能力的过程,就是不断与各种层面的单点斗争的过程。

我们不妨以一个生活中最熟悉的场景作为贯穿始终的例子,来推演系统架构从简单到复杂,所遇到的问题。

上图展示的是用支付宝买早餐的情景,当然角色是虚构的。

最早支付宝只是从淘宝剥离的一个小工具系统,处于单体应用时代。这个时候移动支付当然还没出现,我们的例子仅用于帮助分析问题,请忽略这个穿帮漏洞。

假设图中的场景发生在北京,而支付宝系统是部署在杭州的机房。在小王按下“支付”按钮的一瞬间,会发生什么事情呢?

支付请求要从客户端发送到服务端,服务端最终再把结果返回客户端,必然会有一次异地网络往返,耗时大约在数十毫秒的数量级,我们用红色线表示。应用进程内部会发生很多次业务逻辑运算,用绿色圈表示,不涉及网络开销,耗时忽略不计。应用会访问多次数据库,由于都在部署在同一个机房内,每次耗时按一毫秒以下,一笔支付请求按 10 次数据库访问算(对于支付系统来说并不算多,一笔业务可能涉及到各种数据校验、数据修改)。耗时大头在无可避免的用户到机房物理距离上,系统内部处理耗时很小。

到了服务化时代,一个好的 RPC 框架追求的是让远程服务调用像调本地方法一样简单。随着服务的拆分、业务的发展,原本进程内部的调用变成了网络调用。由于应用都部署在同一个机房内,业务整体网络耗时仍然在可接受范围内。开发人员一般也不会特别在意这个问题,RPC 服务被当成几乎无开销成本地使用,应用的数量也在逐渐膨胀。

服务化解决了应用层的瓶颈,紧接着数据库就成为制约系统扩展的瓶颈。虽然我们本次重点讨论的是服务层,但要讲单元化,数据存储是无论如何绕不开的话题。这里先插播一下分库分表的介绍,作为一个铺垫。

通过引入数据访问中间件,可以实现对应用透明的分库分表。一个比较好的实践是:逻辑拆分先一步到位,物理拆分慢慢进行。以账户表为例,将用户 ID 的末两位作为分片维度,可以在逻辑上将数据分成 100 份,一次性拆到 100 个分表中。这 100 个分表可以先位于同一个物理库中,随着系统的发展,逐步拆成 2 个、5 个、10 个,乃至 100 个物理库。数据访问中间件会屏蔽表与库的映射关系,应用层不必感知。

解决了应用层和数据库层单点后,物理机房又成为制约系统伸缩能力和高可用能力的最大单点。

要突破单机房的容量限制,最直观的解决办法就是再建新的机房,机房之间通过专线连成同一个内部网络。应用可以部署一部分节点到第二个机房,数据库也可以将主备库交叉部署到不同的机房。

这一阶段,只是解决了机房容量不足的问题,两个机房逻辑上仍是一个整体。日常会存在两部分跨机房调用:

  1. 服务层逻辑上是无差别的应用节点,每一次 RPC 调用都有一半的概率跨机房;