Github :https://github.com/alibaba/fescar
中文文档:https://github.com/alibaba/fescar/wiki/Home_Chinese
demo:https://github.com/fescar-group/fescar-samples
dubbo官网使用fecar的案例
从Github上下载的fescar目测是没有example的,所以需要从demo地址里下载demo。关于官网的案例就不赘述了,直接开始运行步骤。
个人使用的是STS,解压从第三个地址下载的fescar-samples压缩包。然后用STS导入。
没必要全部导入,我测试的是dubbo例子。springboot的例子还没测,有兴趣的自己动手。
从第一个地址下载的zip解压后,导入该目录下的所有工程。主要是server工程里有依赖的版本号需要从父工程获取,如果只导入server会提示Maven找不到依赖的。所以干脆全导入即可。
官网给的例子是在一个库里创建三张表,然后配置相同的数据源,追求简单。我选择创建三个库,分别将账户、订单、库存的表放到各自对应的数据库中,也是为了更贴近实际生产的情况模拟。
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
UNDO_LOG 此表用于 Fescar 的AT模式。
在fescar_storage库创建表storage_tbl
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;
在fescar_order库创建表order_tbl
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;
在fescar_account库创建表account_tbl
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;
如图,修改数据库配置文件。三个数据源对应的数据库url以及账号密码。
前三个服务的启动顺序随意,启动后最终启动DubboBusinessTester接口运行。运行后程序会给账户表和库存表各自初始化一条数据。
此时运行DubboBusinessTester的main方法后控制台会报错
原因是demo里的purchase方法中官方抛出一个异常。
@Override
@GlobalTransactional(timeoutMills = 300000, name = "dubbo-demo-tx")
public void purchase(String userId, String commodityCode, int orderCount) {
LOGGER.info("purchase begin ... xid: " + RootContext.getXID());
storageService.deduct(commodityCode, orderCount);
orderService.create(userId, commodityCode, orderCount);
throw new RuntimeException("xxx");
}
注释掉后再运行就可以看到事务全部提交,三个库的业务表都更新或插入了相关记录。至此,官方demo运行完毕。
当代码执行这一步之前,查看数据库。
库存表的count减少了2变为98
账户表金额money减少了400变为599
并且订单表生成了一条记录
由此证明,本地事务已经提交了。但是全局事务还是可以回滚的,继续执行,会抛出事先写好的异常。全局事务回滚,再看数据库时,三张表的数据全部回滚为初始状态。
public class TransactionalTemplate {
/**
* Execute object.
*
* @param business the business
* @return the object
* @throws ExecutionException the execution exception
*/
public Object execute(TransactionalExecutor business) throws TransactionalExecutor.ExecutionException {
// 1. get or create a transaction
GlobalTransaction tx = GlobalTransactionContext.getCurrentOrCreate();
// 2. begin transaction开启事务
try {
tx.begin(business.timeout(), business.name());
} catch (TransactionException txe) {
throw new TransactionalExecutor.ExecutionException(tx, txe,
TransactionalExecutor.Code.BeginFailure);
}
Object rs = null;
try {
// Do Your Business业务处理
rs = business.execute();
} catch (Throwable ex) {
// 3. any business exception, rollback.
try {
//回滚
tx.rollback();
// 3.1 Successfully rolled back
throw new TransactionalExecutor.ExecutionException(tx, TransactionalExecutor.Code.RollbackDone, ex);
} catch (TransactionException txe) {
// 3.2 Failed to rollback
throw new TransactionalExecutor.ExecutionException(tx, txe,
TransactionalExecutor.Code.RollbackFailure, ex);
} finally {
GlobalTransactionContext.clean();
}
}
// 4. everything is fine, commit.
try {
tx.commit();
} catch (TransactionException txe) {
// 4.1 Failed to commit
throw new TransactionalExecutor.ExecutionException(tx, txe,
TransactionalExecutor.Code.CommitFailure);
} finally {
GlobalTransactionContext.clean();
}
return rs;
}
}