通过《消费者实现应用内分布式事务》、《生产者实现应用内分布式事务管理》、《实现持久订阅消费者》三个章节的实践,其实我们已经可以通过消息队列实现多应用的分布式事务,应用内的事务保证了消息不会被重复生产消费、持久化订阅保证了消息一定会被消费(进入死信队列特殊处理),但其对于业务来说耦合性还是太强,在进行业务处理的同时消息处理名,其采用的仍然是应用内的事务处理,并不适合在大型高性能高并发系统实践,那么本章将通过本地事务+消息队列+外部事件定义表+定时任务实现解耦。
(目前主要实现微服务下最终一致性事务的方案主要是:可靠事件;补偿模式;TCC(Try-Confirm-Cancel);三种,本案例为可靠事件方式)
场景描述:
本场景就拿最常用的转账业务阐述:
在工行ICBC有账号Card001,其中存于500元;
在中行BOC有账号Card002,其中也存有500元;
此时从Card001账号转账至Card002账号300元。
系统设计:
工行系统ICBCPro,该工程主要实现两个功能(实现转出金额生成转账事件;定时任务发出转账事件至消息队列),主要参考《生产者实现应用内分布式事务管理》实现;
中行系统BOCPro,该工程主要实现两个功能(从消息队列下载转账事件;定时任务对转账事件处理并更新转入账号金额),主要参考《消费者实现应用内分布式事务》实现;
此场景仅需要通过P2P消息模式即可。
构建ICBCPro工程
A、实现转出金额生成转账事件
1、构建数据库相关表以及基础数据:
转出账号数据
转出事件记录
消息队列
消息控制台
2、执行单元测试代码实现转账,此时账户扣除与转账事件记录均在本地事务内:
//ICBC中账户card001转出300元
@Test
public void tranfer(){
EventLog eventLog = new EventLog();
eventLog.setAmount(new BigDecimal(300));
eventLog.setFromcard("card001");
eventLog.setTocard("card002");
eventLog.setEventstate(EventState.NEW);
eventLog.setTransferDate(new Date());
eventLogService.transfer(eventLog,new BigDecimal(300));
}
账户信息:
事件记录
B、定时任务发出转账事件至消息队列
对于事件记录表,我们可以定义一个定时任务,将所有的NEW状态事件全部发出,此时需要保证消息的可靠性,采用XA事务实现,但已经不影响我们业务的响应了,实现解耦、快速响应,下面贴出核心实现代码:
1、首选实现数据排它锁场景下的查询与更新:
/**
* 在排它锁场景下数据更新,保证数据的可靠性
*/
@Override
public void updateEventstateById(String id, EventState eventState) {
EventLog eventLog=findEventLog4Update(id);
eventLog.setEventstate(eventState);
emJ1.merge(eventLog);
}
/**
* 实现排它锁查询
*/
@Override
public