Apache-Seata 拯救分布式系统数据一致性的开源神器
前言
Seata是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务。它主要用于解决分布式系统中数据一致性的问题。在分布式系统中,一个业务操作可能会涉及多个服务(如订单服务、库存服务、支付服务等),这些服务可能分布在不同的服务器上,使用不同的数据库。Seata就是要保证在这样复杂的环境下,这些服务操作的数据能够保持一致性,就像在单体应用中一样。
在一个电商系统中,用户下单购买商品这个操作涉及订单创建、库存扣减和支付处理三个服务。如果没有分布式事务管理,当订单创建成功,库存扣减也成功,但支付处理出现故障时,就会出现数据不一致的情况,比如用户没有付款却成功下单且库存减少了。Seata可以避免这种情况的发生。
发展历史
早在 2007 年,阿里巴巴和蚂蚁集团内部开发了分布式事务中间件,用于解决电商、支付、物流等业务场景中应用数据的一致性问题。内部项目分别被称为 TXC (Taobao Transaction Constructor)/XTS(eXtended Transaction Service),该项目几乎在每笔订单的交易支付链路几乎都有使用。
自 2013 年以来,阿里巴巴和蚂蚁集团已在阿里云和金融云上向企业客户分别发布了分布式事务云服务产品 GTS(global transaction service)/DTX(Distributed Transaction-eXtended),在各个行业领域积累了大量用户。
2019 年 1 月,阿里巴巴集团正式开源了该项目,项目命名为 Fescar (Fast & Easy Commit and Rollback))。项目开源以来,它受到了众多开发人员的热烈欢迎和赞扬,开源一周收获了超 3k star,曾一度蝉联 GitHub Trending 排行榜第一。
2019 年 4 月,蚂蚁集团数据中间件团队加入了 Fescar 社区。为了创建一个更加开放和中立的社区,Fescar 改名为 Seata(Simple Extensible Autonomous Transaction Architecture),代码仓库从 Alibaba organization 迁移到其独立的 Seata organization。
2019 年 12 月,Seata 开源项目正式发布 1.0.0 GA 版本,标志着项目已基本可生产使用。
2023 年 10 月,为了更好的通过社区驱动技术的演进,阿里和蚂蚁集团正式将 Seata 捐赠给 Apache 基金会,该提案已通过了 Apache 基金会的投票决议,Seata 正式进入 Apache 孵化器。
Seata的架构和核心组件
TC(Transaction Coordinator) 事务协调器
这是Seata的核心组件之一,它负责协调和管理全局事务。TC维护着全局事务的状态信息,比如事务是处于开始、提交还是回滚阶段。所有参与分布式事务的服务都需要和TC进行通信。例如,当一个服务想要开启一个全局事务时,它会向TC发送请求,TC会为这个事务分配一个全局唯一的XID(Transaction ID),并记录事务的开始状态。
TM(Transaction Manager) 事务管理器
通常位于业务服务端,它主要负责定义全局事务的范围,即哪些本地事务应该被纳入到一个全局事务中。比如在上述电商系统中,订单服务中的业务逻辑可能会通过TM来开启一个包含订单创建、库存扣减和支付处理的全局事务。TM会向TC发送全局事务的开始、提交或者回滚请求。
RM(Resource Manager) - 资源管理器
RM主要负责管理和执行本地事务,并向TC报告本地事务的状态。每个参与分布式事务的数据库或者其他资源都有对应的RM。以库存服务为例,库存数据库对应的RM会在接到TC的指令后,执行库存扣减的本地事务,然后将事务执行成功或者失败的状态报告给TC。
Seata的工作模式
AT模式(Automatically Transaction)
这是Seata默认的工作模式,也是使用比较广泛的一种模式。在AT模式下,Seata会在业务代码执行SQL语句时,自动对数据进行“快照”。例如,当库存服务要扣减库存时,Seata会在执行扣减操作之前,记录下库存数据的原始状态(快照)。如果后面的全局事务需要回滚,Seata可以根据这个快照将数据恢复到原来的状态。在整个过程中,对业务代码的侵入性较小,开发人员只需要按照正常的方式编写业务逻辑和SQL语句,Seata会自动处理分布式事务。
TCC模式(Try - Confirm - Cancel)
这种模式相对于AT模式,对业务代码有一定的侵入性。它把一个分布式事务分为三个阶段:Try、Confirm和Cancel。在Try阶段,业务服务会进行资源的预留,比如在支付服务中,可能会冻结用户账户中的相应金额。如果所有参与事务的服务在Try阶段都成功,那么就进入Confirm阶段,在这个阶段会真正执行业务操作,如扣除冻结的金额完成支付。如果在Try阶段或者Confirm阶段出现问题,就会进入Cancel阶段,对之前预留的资源进行释放,比如解冻用户账户中的金额。
XA模式
XA模式是基于数据库的分布式事务协议。Seata在这种模式下,利用数据库本身提供的XA事务接口来实现分布式事务。不过,这种模式对数据库的支持要求比较高,而且性能相对AT和TCC模式可能会稍差一些。它主要适用于一些对数据一致性要求极高,并且数据库本身支持XA协议的场景。
Seata的优势
- 高可用性:Seata的各个组件(TC、TM、RM)可以进行集群部署,这样即使某个节点出现故障,整个分布式事务处理系统仍然能够正常工作。例如,通过部署多个TC节点,并使用负载均衡机制,可以保证事务协调功能的持续可用性。
- 易于集成:Seata对多种主流的编程语言和框架(如Java、Spring Cloud等)有很好的支持,可以方便地集成到现有的微服务架构中。开发人员只需要添加相关的依赖和简单的配置,就可以使用Seata来管理分布式事务。
- 性能优化:通过各种优化机制,如缓存机制、异步处理等,Seata可以在保证数据一致性的同时,提供较好的性能。例如,在AT模式下,Seata对数据快照的缓存可以减少不必要的数据读取操作,提高事务处理效率。
应用场景
- 电商系统:如前面提到的,电商系统中涉及订单处理、库存管理、支付等多个服务之间的复杂交互,Seata可以有效地保证这些服务之间的数据一致性。
- 金融系统:在金融交易、资金转账等场景中,对数据的准确性和一致性要求极高。Seata可以确保多个金融服务(如账户服务、交易服务等)之间的分布式事务的正确处理,防止出现资金错误等问题。
- 物流系统:物流系统包括订单物流信息更新、仓库发货、运输调度等多个环节,这些环节可能涉及不同的服务和数据库。Seata可以保证这些环节在分布式环境下的数据一致性,提高物流系统的可靠性。
Demo
用户购买商品的业务逻辑。整个业务逻辑由 3 个微服务提供支持:
- 仓储服务:对给定的商品扣除仓储数量。
- 订单服务:根据采购需求创建订单。
- 帐户服务:从用户帐户中扣除余额。
仓储服务
public interface StorageService {/*** 扣除存储数量*/void deduct(String commodityCode, int count);
}
订单服务
public interface OrderService {/*** 创建订单*/Order create(String userId, String commodityCode, int orderCount);
}
帐户服务
public interface AccountService {/*** 从用户账户中借出*/void debit(String userId, int money);
}
主要业务逻辑
public class BusinessServiceImpl implements BusinessService {private StorageService storageService;private OrderService orderService;/*** 采购*/public void purchase(String userId, String commodityCode, int orderCount) {storageService.deduct(commodityCode, orderCount);orderService.create(userId, commodityCode, orderCount);}
}
public class OrderServiceImpl implements OrderService {private OrderDAO orderDAO;private AccountService accountService;public Order create(String userId, String commodityCode, int orderCount) {int orderMoney = calculate(commodityCode, orderCount);accountService.debit(userId, orderMoney);Order order = new Order();order.userId = userId;order.commodityCode = commodityCode;order.count = orderCount;order.money = orderMoney;// INSERT INTO orders ...return orderDAO.insert(order);}
}
SEATA 的分布式交易解决方案
@GlobalTransactional
public void purchase(String userId, String commodityCode, int orderCount) {......
}
RocketMQ 接入 Seata
使用RocketMQ
作为Seata分布式事务的参与者很简单,先确保已经引入了seata-all
或者seata的springboot-starter
依赖。
然后通过SeataMQProducerFactory
创建生产者,然后通过 SeataMQProducer
可以直接使用 RocketMQ
发送消息。以下是一个例子:
public class BusinessServiceImpl implements BusinessService {private static final String NAME_SERVER = "127.0.0.1:9876";private static final String PRODUCER_GROUP = "test-group";private static final String TOPIC = "test-topic";private static SeataMQProducer producer= SeataMQProducerFactory.createSingle(NAME_SERVER, PRODUCER_GROUP);public void purchase(String userId, String commodityCode, int orderCount) {producer.send(new Message(TOPIC, "testMessage".getBytes(StandardCharsets.UTF_8)));//do something}
}
这样达到的效果是:生产消息作为Seata分布式事务的参与者RM,当全局事务的一阶段完成,这个MQ消息会根据二阶段要求commit/rollback进行消息的提交或撤回,在此之前消息不会被消费。 注: 当前线程中如果没有xid,该producer会退化为普通的send,而不是发送半消息
资料
- https://seata.apache.org/zh-cn/
- https://www.sofastack.tech/blog/open-source-together-seata-enters-apache-incubator/