备注:消息最终一致性的补偿机制、分布式事务场景不在本规范范畴内
问题
业务场景
业务需求上经常会有一些边缘操作,比如主流程操作A:用户报名课程操作入库,边缘操作B:发送邮件或短信通知。
业务要求
-
操作A操作数据库失败后,事务回滚,那么操作B不能执行。
-
操作B执行成功的话,操作A一定是执行成功的,反之不成立,加上补偿机制可实现在后续某个时间点操作B是成功的。
实现方案
1、使用TransactionSynchronizationManager在事务提交之后操作
@Transactional
public void insert(TechBook techBook){
bookMapper.insert(techBook);
// send after tx commit but is async
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
@Override
public void afterCommit() {
System.out.println("send email after transaction commit...");
}
}
);
}
2、使用@TransactionalEventListener处理数据库事务提交成功后再执行操作
@Transactional
public void finishOrder(Order order){
// 修改订单成功
updateOrderSuccess(order);
// 发布 Spring Event 事件
applicationEventPublisher.publishEvent(new MyAfterTransactionEvent(order));
}
@Slf4j
@Component
private static class MyTransactionListener {
@Autowired
private MqService mqService;
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
private void onHelloEvent(MyAfterTransactionEvent event) {
Order order = event.getOrder();
mqService.send(order);
}
}
// 定一个事件,继承自ApplicationEvent
private static class MyAfterTransactionEvent extends ApplicationEvent {
private Order order;
public MyAfterTransactionEvent(Object source, Order order) {
super(source);
this.order = order;
}
public Order getOrder() {
return order;
}
}
分析对比
TransactionSynchronizationManager
-
优点:适合应用在旧逻辑上,降低改造成本
-
缺点:该方式对代码的耦合度较高,代码不够优雅
TransactionalEventListener
-
优点:结构上比 TransactionSynchronization 优雅,并且可结合领域,在监听触发多个领域事件的发送,建议在后续新发消息中采用
-
缺点:对旧代码逻辑优雅不起来,单个领域事件由于触发点众多,可能需要定义多个事件及监听
结论
旧有消息发送采用TransactionSynchronizationManager覆盖,新增消息发送用TransactionalEventListener设计解决
网友评论