随着业务的快速发展、业务复杂度越来越高,传统单体应用逐渐暴露出了一些问题,例如开发效率低、可维护性差、架构扩展性差、部署不灵活、健壮性差等等。
微服务架构是一个分布式的系统,按业务进行划分为独立的服务单元,解决单体系统的不足,同时也满足越来越复杂的业务需求。每个微服务仅关注于完成一件任务并很好地完成该任务。
微服务架构的特点
微服务架构的优势非常明显,在近些年迅猛发展。
- 将复杂的业务拆分成多个小的业务,能够达到更好的业务复用,有利于人员组织分工
- 服务独立部署,独立扩容,每个服务的修改和部署对其他服务没有影响
- 每个服务可以根据业务场景选取合适的编程语言和数据库
微服务有以上的优势,但是微服务也带来不少的新问题,例如:
- 服务数量众多,其测试、部署、监控等都变的更加困难。
- 单体应用拆分为分布式系统后,进程间的通讯机制和故障处理措施变的更加复杂
- 系统微服务化后,原先是一个服务内部的本地数据库事务,被拆到了多个服务,需要在分布式环境下保证事务的一致性
上述的各项问题中,1、2都可以通过近几年涌现的各项微服务技术解决,例如Kubernetes提供了服务发现、服务治理等。因此分布式事务已经成为微服务落地最大的阻碍,也是最具挑战性的一个技术难题。下面将深入和大家探讨微服务架构下,分布式事务的解决方案。
从本地事务到分布式事务的演变
我们那转账作为例子,A需要转100元给B,那么需要给A的余额-100元,给B的余额+100元,单体模式下,可以通过本地事务解决。
本地事务
把多条语句作为一个整体进行操作的功能,被称为数据库_事务_。数据库事务可以确保该事务范围内的所有操作都可以全部成功或者全部失败。如果事务失败,那么效果就和没有执行这些SQL一样,不会对数据库数据有任何改动。
数据库事务具有ACID这4个特性:
- A:Atomic,原子性,将所有SQL作为原子工作单元执行,要么全部执行,要么全部不执行;
- C:Consistent,一致性,事务完成后,所有数据的状态都是一致的,即A账户只要减去了100,B账户则必定加上了100;
- I:Isolation,隔离性,如果有多个事务并发执行,每个事务作出的修改必须与其他事务隔离;
- D:Duration,持久性,即事务完成后,对数据库数据的修改被持久化存储。
分布式事务典型场景
银行跨行转账业务是一个典型分布式事务场景,假设A需要跨行转账给B,那么就涉及两个银行的数据,无法通过一个数据库的本地事务保证转账的正确性,只能够通过分布式事务来解决。
将服务拆分为微服务时,遇见类似需要分布式事务的场景非常多,虽然微服务最佳实践建议尽量规避分布式事务,但是在很多业务场景,分布式事务是一个绕不开的技术问题。
分布式事务方案
分布式事务模式常见的有XA、TCC、SAGA、可靠消息,下面进行简短的介绍
两阶段提交/XA
XA是由X/Open组织提出的分布式事务的规范,XA规范主要定义了(全局)事务管理器(TM)和(局部)资源管理器(RM)之间的接口。本地的数据库如MySQL在XA中扮演的是RM角色。
XA一共分为两阶段:
第一阶段(prepare):即所有的参与者RM准备执行事务并锁住需要的资源。参与者ready时,向TM报告已准备就绪。
第二阶段(commit/rollback):当事务管理者(TM)确认所有参与者(RM)都ready后,向所有参与者发送commit命令。
目前主流的数据库基本都支持XA事务,包括MySQL、Oracle、SQLServer、PostgreSQL。
一个成功完成的XA事务时序图如下:
TCC事务方案
TCC方案其实是XA提交的一种改进。其将整个业务逻辑的每个分支显式的分成了Try、Confirm、Cancel三个操作。Try部分完成业务的准备工作,confirm部分完成业务的提交,cancel部分完成事务的回滚。
事务开始时,业务应用会向事务协调器注册启动事务。之后业务应用会调用所有服务的try接口,完成一阶段准备。之后事务协调器会根据try接口返回情况,决定调用confirm接口或者cancel接口。如果接口调用失败,会进行重试。
一个成功完成的TCC事务时序图如下:
SAGA事务方案
Saga和TCC一样,也是最终一致性事务、柔性事务。Saga的本质就是把一个长事务分隔成一个个小的事务,每个事务都包含一个执行模块和补偿模块。
Saga没有try,直接提交事务,可能出现脏读的情况,在某些对一致性要求较高的场景下,是不可接受的。
在启动一个Saga事务时,事务管理器会告诉第一个Saga参与者,也就是子事务,去执行本地事务。事务完成之后Saga的会按照执行顺序调用Saga的下一个参与的子事务。这个过程会一直持续到Saga事务执行完毕。
如果在执行子事务的过程中遇到子事务对应的本地事务失败,则Saga会按照相反的顺序执行补偿事务。
一个成功完成的SAGA事务时序图如下:
可靠消息
消息一致性方案是通过消息中间件保证上下游应用数据操作的一致性。基本思路是将本地操作和发送消息放在一个本地事务中,保证本地操作和消息发送要么两者都成功或者都失败。下游应用向消息系统订阅该消息,收到消息后执行相应操作。
RocketMQ 提供了典型的可靠消息接口,可以参考。
分布式事务开源项目
当前的分布式事务领域,有java语言的开源项目,以seata为代表。在非Java领域,Go语言的 DTM 是代表项目。 DTM 支持XA、TCC、SAGA、可靠消息,架构图如下:
图中的各角色与XA模型中的角色模型一致,分别解释如下:
- AP应用程序(定义和提交事务,当前支持Go语言,即将支持Nodejs、Python、PHP、Rust等)
- RM资源管理器(负责管理本地事务,不限语言,只要提供了http相关的接口即可)
- TM事务管理器(DTM,协调全局事务,进行提交以及回滚)
在上述的架构图中,AP通过DTM提供的分布式事务接口,与RM和TM交互,对现有的微服务,侵入很小。
另外在实际的业务中,AP和RM角色可能会有重叠,例如TCC模式下,AP可能有自己的本地事务,也会注册并调用其他事务分支。