今天本来准备完善自己的微服务框架中的数据库访问方面内容,一大早集成好了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
一种分布式事务解决方案,具有高性能和易于使用的微服务架构。
让我们想象一下传统的单机应用程序。其业务由3个模块构成。他们使用单个本地数据源。
当然,本地事务将保证数据的一致性。
微服务架构的情况发生了变化。上面提到的3个模块被设计为3个不同数据源之上的3个服务。本地事务自然保证每个服务中的数据一致性。
但整个业务逻辑范围如何呢?
Fescar只是解决上述问题的方法。
首先,如何定义分布式事务?
我们说,分布式事务是一个全局事务,由一批分支事务组成,通常分支事务只是本地事务。
Fescar有3个基本组件:
Fescar管理分布式事务的典型生命周期:
用户购买商品的业务逻辑。整个业务逻辑由3个微服务提供支持:
public interface StorageService {
/**
* deduct storage count
*/
void deduct(String commodityCode, int count);
}
public interface OrderService {
/**
* create order
*/
Order create(String userId, String commodityCode, int orderCount);
}
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);
}
我们只需要一个@GlobalTransactional
关于业务方法的注释:
@GlobalTransactional
public void purchase(String userId,String commodityCode,int orderCount){
......
}
注意:实际上,示例用例中的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 ” />
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
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;
sh fescar-server.sh $ LISTEN_PORT $ PATH_FOR_PERSISTENT_DATA
例如
sh fescar-server.sh 8091 / home / admin / fescar / data /
示例工程:https://github.com/fescar-group/fescar-samples
- 启动AccountService
- 启动StorageService
- 启动OrderService
- 运行BusinessService进行演示测试