演讲嘉宾简介:
经国,蚂蚁金服资深技术专家,毕业于浙江大学。2014年加入蚂蚁金服,先后负责过支付宝的单元化、弹性、去ORACLE等架构升级,担任多年支付宝双十一、双十二、新春红包大型活动等技术保障负责人,现为蚂蚁金服数字金融线担任技术风险架构师,负责高可用架构、技术风险平台、应急快反等技术底盘的建设。
**以下内容根据演讲视频以及PPT整理而成。
本次分享主要围绕以下五个方面:
• 应用架构演进路径
• 云原生时代的技术福利
• 高可用架构的设计原则
• 经典案例的设计
• 未来思考**
微服务是当下非常热门的一种架构,阿里目前正在从SOA架构体系向微服务架构迁移。同时整个软件应用研发开始进入云原生时代。在这些技术演进背景下讨论如何更好地实现稳定且高可用的架构方案,保证应用持续可用非常有必要。
一、应用架构演进路径
支付宝最开始是一个单体应用。随着业务不断发展,支付宝拆分成了多个服务,衍生出了若干代架构。微服务是服务化后的进一步演进,服务的粒度比服务化更细,具有很好的流量管控机制,中间件和编程模型。云原生的发展使Serverless也得到了发展,FAAS是Serverless的一种典型实现,能够以非常小的成本搭建小程序。另外,低代码和无代码现在也非常流行。
基础设施同样也得到了很好的发展。最开始,单体架构是托管式的,通常将应用程序托管给电信运营商的某个机房,在物理机上运行单体应用。后来,基础设施开始向虚拟化演进,比如VMware等公司在物理机上构建虚拟化层和虚拟机,在虚拟机上运行操作系统。再后来,云化结合虚拟化技术和基础设施,在虚拟机上运行Docker进程,在Docker进程中运行应用程序。现在,根据云通未来的理念,包括蚂蚁在内的很多公司都在实现上云。蚂蚁的基础设施已经完全实现了云化,包括公有云和私有云。从架构角度来看,蚂蚁的基础设施就是在云底座上运行一层K8S,在Pod里运行APP,其上层对应着FAAS、网格化、Service mesh、微服务等应用程序。
作为一个复杂的业务应用,如何实现技术支撑对蚂蚁非常关键。蚂蚁目前正在尝试许多FAAS相关的技术选型,但从更大的范围来看,微服务仍然是主流的选择。蚂蚁所使用的微服务架构本身也在不断演进,比如把和业务无关的功能下沉到Sidecar,将数据库的一些中间件mesh化等。尽管从应用研发的角度看,蚂蚁所使用的架构风格相比于原来发生了很大的变化,但从整体趋势来看,最主要的变化仍在于将和业务能力无关并且和基础设施相关的能力下沉。
二、云原生时代的技术红利
云原生已经演化成了一个非常庞大的生态。这张图囊括了云原生里非常多的内容。从设计高可用且稳定性高的应用的角度来看,相关的内容包括数据库技术、云原生存储、Service mesh、可观测性、Serverless等。
数据库技术
应用开发不仅需要关注业务逻辑,还需要关注数据,将领域模型、业务流程、配置等数据以Online形式存储。近几年,数据库技术发生了很大的变化,HBase等分布式数据库提供的强一致保证给云原生应用的设计和开发带来了许多好处。此前,应用设计需要考虑读写分离,需要连接不同的数据源处理读写分离的结果,并且这些功能都需要写入应用程序代码。而现在,这些功能可以直接利用数据库的水平扩展能力来实现,数据库技术的发展极大地避免了我们直接和数据层交互。
云原生存储
蚂蚁的大部分业务已经实现云盘存储,也就是将日志存储到远方磁盘。此前,单体应用通常将数据存储于本机磁盘,并且不提供其它冗余备份;而云盘则默认提供数据冗余,这在基于日志不可靠假设进行应用设计时带来了很多变化。
可观测性
应用程序存在着许多监控操作。随着Sidecar将很多能力下沉,包括访问RPC和DB,此前以中间件形式实现的监控操作,现在则以切面形式来实现,二者有很大的区别。
云原生时代的技术红利
云原生时代带来的最大变化在于基础设施和业务逻辑的真正解耦。此前,中间件逻辑存在于应用程序的进程中,而现在压测、限流等都可以在Sidecar中实现,从而解耦了基础设施和业务逻辑。然而,这种解耦是不断进行着的,即使在未来也很难做到业务完全不需要关注基础设施。比如,业务流量应该多大,DB节点应该设置多少个,业务流量和DB的设计是否符合要求。比如,同步的关键业务对应的流量链路和异步化任务可能运行在同一个节点中,那么如何实现二者流量隔离就是一个难题。再比如,如何结合基础设施和业务要求进行部署,保证内存和CPU合理分配。在基础设施对这些内容无感知的前提下,如何自动化地进行混布和限流。从应用研发的视角来看,此前需要写很多代码来实现业务逻辑和基础设施交互;而现在相应的代码量会减少很多。
三、高可用架构设计的设计原则
三个架构设计原则:可观测、可灰度、可回滚
高可用设计主要有三个原则,包括可观测、可灰度、可回滚。大部分云研发场景并不需要关注可演变,但在一些特殊场景下可演变仍然是一个问题。比如,蚂蚁的金融业务需要和一些机构进行信息交换,由于金融领域的RPC交互常常使用标准化文件,在文件场景下如何保证可演变也是高可用设计需要关注的内容。典型的用于提高架构可用性的设计原则包括四种:解耦、冗余、异构和异步。
解耦
在数据库方面,如何解耦核心和非核心业务的DB。在业务链路方面,由于微服务具有复杂的业务场景和节点,这些业务场景和节点间如何混合,业务节点如何支撑业务链路,容量不足时哪些业务场景应优先通过,限流时应优先限流哪些业务等也是解耦需要关注的内容。总体而言,解耦原则要求将最核心的业务链路隔离出来,使其与其它业务间的耦合尽可能小。
冗余
应用程序可能有多个机房,如果多个机房间存在数据冗余,那么一个位置的错误就能够由另一个位置的数据来弥补,从而保证系统的持续可用。读写分离也是一种冗余设计,缓存和DB间存在数据冗余,当缓存宕机时,可以从DB回源到缓存。
异构
如果多个DB间是同构的,那么可能存在一些情况使得中心化的内容同时挂掉;但如果DB是异构的,比如使用的数据库版本不同,那么这些数据库同时挂掉的情况则非常少。
异步
异步要求将业务流程的核心部分抽象出来,使其不与非核心的内容耦合。从理论上看,核心部分越精炼,系统出错误的概率越小。比如,支付宝的支付业务背后有一百多个业务处理节点,在微服务时代,其背后可能有几百个甚至上千个业务节点,假设每个节点的可用率都是5个9,把几百个甚至几千个节点的可用率乘起来就会使得整体的可用率非常小。但通过移除和核心业务无关的内容,从而减小节点数量后,系统的可用率就会大幅增高。上图的左右两边从不同角度对微服务的高可用设计进行了分析。左边是微服务设计应具有的能力,右边是设计高可用微服务架构时应遵循的原则。另外,有三块内容实际支撑着一个微服务系统:代码、配置和数据。其中,代码和配置是相辅相成的,代码执行的间歇可能需要修改系统配置;业务数据也非常重要。对这三块内容进行分析是微服务架构高可用设计的重要方面。比如,配置文件的可回滚和可灰度能力。与代码相比,配置文件的可灰度能力不够标准化,尤其是和业务或者运营相关的配置文件;数据也是如此,由于一些数据有多种来源,这些数据的可观测和可演练能力比代码差。从整体而言,代码的相关体系比配置文件和数据更完备。
不过度设计
架构设计的另一个重要原则是不过度设计,高可用架构设计应基于业务需求进行。这是因为高可用设计通常极为复杂。比如,将链路中的一些重要业务解耦出来单独部署,无论其业务流量多大,都为这些业务分配一百个节点,从而降低单节点宕机带来的影响。从本质上来看,这些差异化配置通过付出成本和效率来换取高可用,这就存在着权衡难题。从目前来看,很少有软件系统的高可用能力能够实现5个9,大部分都还停留在理论上达到5个9的状态。
四、经典案例的设计
接下来,以具体的业务场景为例分析典型业务的高可用设计。
手机扫码场景
手机扫码场景要求在手机断网时正常显示其二维码。在这个场景里,真正的业务链路从POS链路开始,有的POS链路需要经过商户的处理系统,有的则直接进入支付宝自身的业务系统。在支付宝端,该链路将从一个统一的网关接入,携带着订单相关信息,比如商户ID等。此后,链路进入一个收单系统,由该系统处理这些相关信息。再往后,链路开始进入交易系统,通过调用交易系统的核心模块完成该业务支付。此外,为了实现扫码,POS系统还需要和支付宝签约以建立授权关系,并且同步商户信息。这个微服务系统所涵盖的高可用需求包括:
其一,该系统主要面向线下场景,商家在未获取现金时不会将商品交给用户。因此,不可用问题对商家的影响很大,比如用户不能正常支付。
其二,该系统的一些节点还需要支撑整个支付宝的业务体系,比如会员信息节点,它需要支持成百上千个与蚂蚁森林相同体量的业务功能,对可用性的要求非常高。
这意味着,在这个微服务系统中,不同业务对可用性的要求不同,因此需要不同的手段实现该系统的可用性需求。
灰度设计
首先,签约业务是异步的,在设计时不应被纳入系统的核心链路。另外,签约服务需要与网关和商户服务进行信息同步,仍可能导致网关和商户服务宕机,比如修改商户的鉴权信息可能使签约不成功。因此,签约服务需要实现灰度设计。
异构设计
其次,如果将一个非常重要的业务从链路中隔离出来,那么为了处理隔离后的节点的宕机事件,就需要一个异构设计使得当其宕机时整个系统还能继续运转,包括数据存储方面和计算能力方面的异构。值得一提的是,现在的分布式数据库能够实现RPO等于0,使得当数据出问题时不发生数据丢失,其基本原理就是存储多个数据副本,并且当数据出现故障时,可以在多个副本间切换,数据恢复速度非常快。然而,这并不意味着高可用设计只需依赖数据库,而不需要额外的容灾能力。事实上,这与系统所要求的高可用级别相关。比如,假设系统所要求的高可用能力级别为5个9,那么即使数据库仅发生一次宕机并且数据恢复失败,那么就无法实现所要求的5个9的高可用能力。高可用设计的一个原则是,核心业务的设计不应信任其所依赖的基础设施。这样,为了提高系统高可用能力,就需要实现应用层容灾。应用层的容灾可能会FO到一个数据库中,数据库版本不同,数据存储类型不同,那么二者同时出现故障的概率就会变得更低。因此,应用层的容灾非常重要。
防热点、读写分离
在service mesh场景下,应用层不再需要关注防热点、读写分离等。几年前,应用层还需要对缓存热点做特殊处理以建设高可用能力,比如在一个缓存节点挂掉时对其进行预热操作。而现在,大多数分布式架构本身就提供了缓存热点能力。此外,大多数分布式数据库本身就使用了读写分离架构,只需稍加配置就可以将数据路由到只读节点。这些都是云原生时代带来的红利之一。
近端
近端在不同场合下可能有不同的名称。比如,如果将会员信息的全部请求都发给应用层,那么其所需要的集群数量将会非常庞大。由于会员信息的查询遵循了较为固定的范式,因此可以将会员信息的查询功能前置到收单服务,从而使收单服务不需要访问会员信息,而可以直接访问会员信息所依赖的服务。这本质上也是通过应用层设计来减小高可用的成本。
总的来说,对于一个给定的业务场景,高可用设计需要分析它的业务特点,可用性的要求,从而设计对应高可用设计的节点。比如,如何设计以查询功能为主的节点,特别是当其所依赖的数据库不被信任时。在微服务架构中进行高可用设计时,应该针对每个节点的特征进行有针对的设计。另外,即使在云原生场景下,也需要通过应用层设计实现防抖、业务隔离、配置灰度设计和应用层容灾。比如,使用FASS能够很容易地实现系统功能,但当系统的可用性要求、业务体量增大时,任何一个抖动都可能影响到整个软件系统的可用能力。高可用设计并不存在标准配置。实际上,高可用设计需要根据业务重要性、能力和效率的分层等进行分层次的设计,最核心业务需要通过一些高复杂度的设计来提高它们的可用性。
业务隔离
线下业务和淘宝业务实际上使用同一个版本进行应用开发和发布,它们的隔离仅仅体现在流量和部署层面。比如,它们所使用的交易服务在开发层面就是完全相同的,仅仅在部署层面将它们的流量分离到不同的服务中。这样,在以业务维度做跨节点的流量绑定时,就需要将几个服务及其节点圈出来进行分流。
首先,需要识别某流量是否与该业务相关;其次,需要将该流量接入到某个特定区域中;然后,该区域还需要将所有与完成该业务相关的节点都包含进来。这种设计需要一定的先验知识,因此需要定义一些元信息,比如流量入口。在确定流量入口后所有和其相关的后续处理节点都应被打标或者以其它方式圈定起来。门面系统后对应着内部系统,包括一系列Http组。在定义好这些后,某个组件会在流量接入后识别门面系统,进而找到该门面系统对应的圈定区域,也就是内部系统,从而完成一个整体上的流量绑定过程。
值得一提的是,这并不是微服务体系下流量绑定的标准配置,而是阿里的应用开发人员和中间件人员提出的业务隔离设计。随着上云等措施,这种设计逐渐内置到基础设施中,成为一个典型的业务隔离设计。
防抖设计
图中的业务场景对应着买家向卖家支付。由于实际支付时,支付操作后置一段时间并不会引发大问题,因此大部分系统在实际运行时,买家向卖家支付的钱都不会即时到账,而需要经过一段时间的累积后再批量到账。本质上,这是一种信息流和业务流的解耦。从业务角度看,钱需要即时从买家流动到卖家,中间还可能有聚合支付等操作。而在实际运行时,系统无需关注具体的支付细节,而只需要通知买家支付成功,通知卖家钱已到账。在不同处理模式下,资金流和信息流的处理流程可能有很大的不同。
防抖设计架构抽象
在具体设计时,一个逻辑支付处理单元实际上被拆分成信息处理单元和资金处理单元。中间的T+x表示,信息处理单元和资金处理单元间可以存在一个较大的时间差。在信息受理时,即使不需要进行实际的资金流处理,也需要明确买家和卖家信息,而这些信息来自会员系统和商家系统。那么,当会员系统宕机时,为了使防抖设计机制可用,就需要异构的设计。实际资金流处理所依赖的数据需要异构出一份以供信息流处理,而不是直接用原来的数据。异构设计使得一份数据宕机不影响整个系统功能的正常运行。除了数据需要进行异构处理外,一些计算规则也需要迁移到信息流处理中,比如商家的店铺信息处理等。数据和计算规则的异构使我们能够实现解耦,这种设计对应着一个标准化的范式。几乎在所有的业务场景都能看到这种设计。
总的来说,将业务最顶端跟信息流相关的逻辑抽象出来,并且将这部分逻辑所依赖的数据异构一部分出来,这就能够使所有的业务实现不依赖于实际的处理逻辑,从而保证底层的任意一个节点发生宕机时整个系统的可用性。
防抖设计架构延展
理论上,这种架构设计是可以扩展的。信息流处理和业务流处理被解耦后就可以被部署在不同的节点中。比如,在国际支付时可以将信息流处理逻辑部署在支付方所在的区域中,这就使得支付操作不需要依赖原来的处理逻辑所部署的机房。需要注意的是,在将数据部署在其它机房时,通常需要一些额外的处理,比如信息安全等内容。
配置灰度设计
某节点的配置数据发生变更可能影响其它与之相关的节点,因此这就需要对这些节点做灰度设计,带来了一个链路级的配置灰度设计问题。
分布式事务中通常有一个发起者和多个参与者。发起者发起一个预提交,在所有参与者确认后,该任务才被执行。这对应着一个二阶段确认过程,即先发起、再确认、后执行。配置灰度设计和分布式事务处理非常类似。首先,签约节点发起一个灰度设计,此后和签约节点相关的节点需要参与进来,一同完成灰度。假设灰度的内容是某仓库信息,此时所有参与节点都需要对本节点下的相关数据进行灰度。在实际进行灰度时,每个节点实现灰度的方式可能不同。比如,商家需要修改DB数据,而会员节点除了需要修改DB数据外,还需要修改对应节点中的缓存。需要注意的是,无论每个节点如何进行具体的灰度操作,各个节点间需要实现灰度的同进同退。
值得注意的是,这是一个最复杂的配置灰度设计,实际上的灰度设计比这个场景要简单地多。
容灾设计
状态型数据和流水型数据
蚂蚁通常按照特性将数据分为状态型数据和流水型数据。所谓流水型数据,即每出现一条数据都将其存入数据库中。比如,订单数据就是一种流水型数据,每出现一条新订单都被存入数据库。流水型数据的大部分业务类型是将数据插入到数据库。另一种数据是状态型数据,通常可以被修改,并且具有生命周期特征,比如会员信息。
状态型数据的容灾设计
这两种数据在进行应用层容灾设计时需要不同的处理方式,状态型数据的容灾设计通常比较困难。比如,会员信息在出现错误时通常不能再写该数据,也不能让该用户重新注册一次支付宝。状态型数据的容灾设计需要以数据库不可靠为前提。数据库通常存在主备库,这两个库通常使用异步复制的方式来保证两个库之间的同步。然而,这种同步通常具有时延特征。当主库宕机时,如果FO库的版本比较旧,就不能直接将FO库作为主库,因为原来的主库上已经有用户修改的内容。如果此时将FO库作为主库继续进行修改,那么最终得到的数据必然不是用户所预期的。一个处理方式是将持续往黑名单库里写差异。当主库宕机时,首先将FO库拉起来,此时,这些差异可能使得FO库中某一部分数据是禁写的。对用户信息这样的业务而言,每天只有很少的数据会发生更改,并且用户信息发生宕机的概率很低,因此这种处理带来的禁写对业务的影响非常小。通过这种方式强一致地写黑名单库,能够近似地实现无损的容灾设计。回切数据库时也是如此。由于FO库已经有很多新的数据内容,因此在回切数据库时需要将这部分数据merge回主库中。
即使在云原生时代,一个业务场景的高可用架构设计也仍然需要许多操作来共同实现。未来,这些与业务无关的设计可能被组件化地沉淀下来,成为基础设施。
五、未来思考
根据业务场景实现个性化高可用设计
高可用设计通常是静态的,它能够被内嵌到架构设计中,被内嵌到基础设施或者中间件中。高可用设计应根据业务场景实现个性化设计。这要求我们不仅需要关注系统当下的业务特点,还需要预测其未来的业务特点,通过各种特性来刻画该业务对用户的可用性影响。这就需要结合各种原子手段以实现业务在当前阶段所需要的高可用能力。未来,可能存在非常智能的系统,能够使用最低成本刻画业务的当前特征,然后自动化地组合一些原子高可用能力最大化系统高可用能力。
5个9的高可用设计
未来还可能实现5个9的高可用。首先,假设存在一个和蚂蚁非常类似但又异构的环境,它们之间完全是去中心化的;其次,这两个环境下的数据规则同步应该是可舍弃的,能够实现FO以及全自动的切换;另外,还需要实现监控,监控也应该是异构的,需要有一个外部系统来观测本系统的行为。