阿里开源分布式事务框架fescar
前言
今天本来准备完善自己的微服务框架中的数据库访问方面内容,一大早集成好了mybatis,然后考虑了下要不要数据库架构,要不要分库分表,事务控制器用啥,思维一下子发散了收不回来了……
然后,抽了几根烟,回顾了一下之前接触过的各种分布式事务方案,JTA,XA,TCC,中间件,工作流等等……
首先PASS了最简单暴力的JTA和XA事务,因为我已经受够了多数据源切换各种神奇的一个库回滚一个库不回滚或者干脆全都没回滚的神奇问题。
然后是TCC,跑到github上看了看bytetcc的开源项目,6天前已经更新到0.5.0了,然而实现方式仍然繁琐,不过还能接受,更新频率挺高,继续观望吧。
消息中间件方案依然是各种复杂,眼睛有点花,想想这没有一个团队帮我搞,不是短期能搞定的,再想想我这可怜的16g内存,虚拟机装了这么多东西,写不动代码了咋搞,跳过……
工作流类的,Saga什么的,感觉跟TCC差不多,也是冗余代码一大堆。
难道,分布式事务就是这么折腾么?
突然,想起还有个阿里的GTS没研究过,看了下介绍(其实重点是看到了示例代码orz),原来分布式事务还可以这么简单,代码侵入几乎没有,而且竟然开源了,也就是标题中的fescar。
github地址:https://github.com/alibaba/fescar
什么是Fescar?
一种分布式事务解决方案,具有高性能和易于使用的微服务架构。
微服务中的分布式事务问题
让我们想象一下传统的单机应用程序。其业务由3个模块构成。他们使用单个本地数据源。
当然,本地事务将保证数据的一致性。
微服务架构的情况发生了变化。上面提到的3个模块被设计为3个不同数据源之上的3个服务。本地事务自然保证每个服务中的数据一致性。
但整个业务逻辑范围如何呢?
Fescar怎么样?
Fescar只是解决上述问题的方法。
首先,如何定义分布式事务?
我们说,分布式事务是一个全局事务,由一批分支事务组成,通常分支事务只是本地事务。
Fescar有3个基本组件:
- 事务协调器(TC):维护全局和分支事务的状态,驱动全局提交或回滚。
- Transaction Manager(TM):定义全局事务的范围:开始全局事务,提交或回滚全局事务。
- 资源管理器(RM):管理分支事务的资源,与TC通信以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。
Fescar管理分布式事务的典型生命周期:
- TM要求TC开始新的全局交易。TC生成表示全局事务的XID。
- XID通过微服务的调用链传播。
- RM将本地事务注册为XID到TC的相应全局事务的分支。
- TM要求TC提交或回滚XID的相应全局事务。
- TC在XID的相应全局事务下驱动所有分支事务以完成分支提交或回滚。
历史
- TXC:Taobao Transaction Constructor。阿里巴巴中间件团队自2014年起启动该项目,以解决因应用程序架构从单机改为微服务而导致的分布式事务问题。
- GTS:Global Transaction Service。TXC作为阿里云中间件产品,新名称GTS自2016年起发布。
- Fescar:我们从2019年开始基于TXC / GTS开源开源项目Fescar,以便在未来与社区密切合作。
如何使用
用例
用户购买商品的业务逻辑。整个业务逻辑由3个微服务提供支持:
- 存储服务:扣除给定商品的存储数量。
- 订单服务:根据购买请求创建订单。
- 帐户服务:借记用户帐户的余额。
建筑
StorageService
public interface StorageService {
/**
* deduct storage count
*/
void deduct(String commodityCode, int count);
}
OrderService
public interface OrderService {
/**
* create order
*/
Order create(String userId, String commodityCode, int orderCount);
}
AccountService
public interface AccountService {
/**
* debit balance of user's account
*/
void debit(String userId, int money);
}
主要业务逻辑
public class BusinessServiceImpl implements BusinessService {
private StorageService storageService;
private OrderService orderService;
/**
* purchase
*/
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);
}
FESCAR的分布式事务解决方案
我们只需要一个@GlobalTransactional
关于业务方法的注释:
@GlobalTransactional
public void purchase(String userId,String commodityCode,int orderCount){
......
}
示例由Dubbo + FESCAR提供
第1步:设置数据库
- 要求:带有InnoDB引擎的MySQL。
注意:实际上,示例用例中的3个服务应该有3个数据库。但是,我们只需创建一个数据库并配置3个数据源即可。
使用刚刚创建的数据库URL /用户名/密码修改Spring XML。
dubbo-account-service.xml dubbo-order-service.xml dubbo-storage-service.xml
< property name = “ url ” value = “ jdbc:mysql:// xxxx:3306 / xxx ” />
< property name = “ username ” value = “ xxx ” />
< property name = “ password ” value = “ xxx ” />
第2步:创建UNDO_LOG表
UNDO_LOG
表是FESCAR AT模式所必需的。
CREATE TABLE `undo_log` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`branch_id` bigint(20) NOT NULL,
`xid` varchar(100) NOT NULL,
`rollback_info` longblob NOT NULL,
`log_status` int(11) NOT NULL,
`log_created` datetime NOT NULL,
`log_modified` datetime NOT NULL,
`ext` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_unionkey` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=159 DEFAULT CHARSET=utf8
第3步:创建表格,例如业务
DROP TABLE IF EXISTS `storage_tbl`;
CREATE TABLE `storage_tbl` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`commodity_code` varchar(255) DEFAULT NULL,
`count` int(11) DEFAULT 0,
PRIMARY KEY (`id`),
UNIQUE KEY (`commodity_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `order_tbl`;
CREATE TABLE `order_tbl` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` varchar(255) DEFAULT NULL,
`commodity_code` varchar(255) DEFAULT NULL,
`count` int(11) DEFAULT 0,
`money` int(11) DEFAULT 0,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `account_tbl`;
CREATE TABLE `account_tbl` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` varchar(255) DEFAULT NULL,
`money` int(11) DEFAULT 0,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
第4步:启动服务器
- 从https://github.com/alibaba/fescar/releases下载服务器包,解压缩。
sh fescar-server.sh $ LISTEN_PORT $ PATH_FOR_PERSISTENT_DATA
例如
sh fescar-server.sh 8091 / home / admin / fescar / data /
第5步:运行示例
示例工程:https://github.com/fescar-group/fescar-samples
- 启动AccountService
- 启动StorageService
- 启动OrderService
- 运行BusinessService进行演示测试