SEATA
是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务。
Seata核心组件
整体的事务逻辑是两阶段提交的模型,主要由三个重要的组件组成:
TC:Transaction Coordinator 事务协调器,管理全局的分支事务的状态,用于全局性事务的提交和回滚。
TM:Transaction Manager 事务管理器,用于开启、提交或者回滚【全局事务】。
RM:Resource Manager 资源管理器,用于分支(即每一个微服务,它是嵌在服务中的)事务上的资源管理,向TC注册分支事务,上报分支事务的状态,接受TC的命令来提交或者回滚分支事务
传统XA协议实现2PC方案的 RM 是在数据库层,RM本质上就是数据库自身;
Seata的RM是以jar包的形式嵌入在应用程序里面
TC 为单独部署的 Server 服务端,TM 和 RM 为嵌入到应用中的 Client 客户端
Seata事务处理
AT模式
该模式适合的场景:
- 基于支持本地
ACID
事务的关系型数据库。 - Java 应用,通过
JDBC
访问数据库。
生命周期描述:
A服务的RM向TC注册分支事务
A服务执行分支事务,对数据库做操作
A服务开始远程调用B服务,并把XID 在微服务调用链路的上下文中传播。
A服务会调用B服务,形成调用链接,这也是分布式事务形成的由来。
B服务的RM向TC注册分支事务,并将其纳入XID对应的全局事务的管辖。
B服务执行分支事务,向数据库做操作
B服务又开始调用C服务,又形成一个调用链,这样ABC三个服务形成调用链。
C服务的RM也向TC注册分支事务,并将其XID纳入全局事务管理中,这样TC会把A、B、C服务串联起来,保证在一个事务里管理。
全局事务调用链处理完毕,TM 根据有无异常向 TC 发起针对 XID 的全局提交(Commit)或回滚(Rollback)决议。
TC 调度 XID 下管辖的全部分支事务完成提交(Commit)或回滚(Rollback)请求。
问题:微服务场景下,配置了统一全局异常处理,导致seata在AT模式下无法正常回滚问题。
原因:服务A调用服务B, 服务B发生异常,由于全局异常处理的存在@ControllerAdvice, seata 无法拦截到B服务的异常,从而导致分布式事务未生效。
解决思路:配置了全局异常处理,所以rpc一定会有返回值, 所以在每个全局事务方法最后, 需要判断rpc是否发生异常发生异常则抛出 RuntimeException。
场景实例
简单的用户下单场景,4个子工程分别是**Bussiness**(事务发起者)、**Order**(创建订单)、**Product**(扣减库存) 和 **Account**(扣减账户余额)。
下图中黄色区域理解为各自独立的微服务被TC纳入了全局事务管理中,整个流程变成了一个原子操作。
用户进行下单,需要进行三个业务调用,Bussiness表示的是业务的发起方,也是我们的一个服务,发起了一个全局事务TM,接着调用商品服务开始扣减库存,Bussiness在调用订单服务,订单服务又会调用账户服务。此时假设商品服务扣减库存成功,订单服务创建订单成功,但是账户服务扣减账户余额失败,出现了异常,此时账户服务的RM即分支事务资源管理器向TC事务报告状态产生异常,此时TC会通知其余的RM回滚事务。当所有的RM均正常则提交事务。
消息事务-异步场景
**描述:**可靠消息模式采用一个可靠的消息中间件作为中介,事物的发起方在完成本地事务后向可靠的消息中间件发起消息,事务消费方在收到消息后处理消息,该方式强调的是双方最终的数据一致性。
**流程:**订单服务将消息发送给订单的消息队列,库存服务去监听订阅订单服务的消息队列,并从中消费消息。这种方式需要考虑消息的生产者发送到消息队列,再由消费者去消费消息,中间都有可能因为网络原因导致数据的不一致性。
本地事务提交后可以使用主动触发方式对本地消息表进行保存与推送。
库存服务在接收到消息并且处理完业务逻辑后,通过消息确认机制,回复ACK保证消息的消费成功。如果库存服务没有回复ACK,则消息中间件在没收到ACK是将进行重复投递。
当消息被成功消费,库存服务可以回调一个订单服务的确认API,订单服务从本地消息表中删除或者更新其状态
在订单服务中,如果重复性把本地消息发到库存服务,则需要消息的消费者(库存服务)提供消息的幂等性支持。
分布式事务的选择
关于一致性:
一致性就是数据保持一致性,在分布式系统中,可以理解为多个节点中的数据的值是一致的,而一致性分为强一致性和弱一致性/最终一致性(本身也是弱一致性的特殊表现形式)。
强一致性:是程度最高的一种要求,也是最难实现的。系统中的某个数据被更新后,后续任何对该数据的操作都是及时更新后的值。
弱一致性:系统中某个数据被更新后,后续对该数据的操作可能得到更新后得知,也可能是更新前的值,但经过“不一致的时间窗口”后,后续对该数据的操作都是更新后的值。
最终一致性:在一段时间后,数据会最终达到一致性状态,这个状态时弱一致性的特殊形式。
场景对比:
模拟一个简单个新用户注册送福利,即营销拉新活动。用户服务与营销服务作为两个独立的服务,假设选择以seata作为分布式事务的解决方案,此时发生的场景:在用户注册环节或者营销服务发放福利环节任意一个环节出异常,那么都会导致用户的注册失败,这就是很不友好了,我们期望的是尽管营销服务出现问题那么应该不会影响用户的注册,福利可以通过后期补发,所以这种场景,消息事务方案更具有优势。
总结
微服务架构下,最好的分布式数据一致性方案就是尽量避免使用分布式事务,但某些场景是不可避免的,因此我们在强一致与最终一致性之间需要做出选择。不管选哪一种方案,在应用时都要谨慎再思考,除特定的数据强一致性场景外,能不用尽量就不要用。因为无论它们性能如何优越,一旦请求链路加入分布式事务整体效率会几倍的下降,在高并发情况下弊端尤为明显。分布式事务和分布式锁一样,能不用就不用实在要用,使用优先是考虑柔性事务,实在无法满足再考虑刚性事务
对于面向C端用户一般推荐使用可靠消息事务模式,如果是同步场景,如管理后台,对一个功能的操作可能涉及到操作多个业务模块,那么可以使用seata。
对于面向C端用户一般推荐使用可靠消息事务模式,如果是同步场景,如管理后台,对一个功能的操作可能涉及到操作多个业务模块,那么可以使用seata。