AOP解决数据库事务与MQ的问题

AOP解决数据库事务与MQ的问题

  • 一.场景
  • 二.问题
    • 1.数据库事务的传播性
    • 2.数据库事务和MQ的一致性
  • 三.分析
    • 1.问题分析
      • 1.1事务传播性分析
      • 1.2一致性分析
    • 2.方案分析
      • 1.1事务和MQ的执行顺序
      • 1.2事件日志记录
      • 1.3大事务的MQ消息发送
      • 1.4 小结
        • 1.4.1 DelayMQ
        • 1.4.2 SendMQMsg
        • 1.4.3 Transactional
        • 1.4.4 EventLog
  • 四.总结

一.场景

项目中复杂的业务流程通常由简单的多个事务组成,每个简单的事务都可能涉及到MQ的发送。场景代码如下。
NotifyApi.java

	@Transactional
	public void paymentNotify(){
		// 支付单操作
		payOrderService.payOrderPaySuccess();
		// 交易单操作
		tradeOrderService.tradeOrderPaySuccess();
	}

PayOrderService.java

	@Transactional
	public void payOrderPaySuccess(){
		// db transaction
		payOrderDas.paySuccess();
		// mq send
		mqService.send();
	}

TradeOrderService.java

	@Transactional
	public void tradeOrderPaySuccess(){
		// db transaction
		tradeOrderDas.paySuccess();
		// mq send
		mqService.send();
	}

二.问题

1.数据库事务的传播性

面对这种业务性比较强的代码,一般是把其它小事务全部放进一个大事务中执行。这里不但要保证小事务单独执行的一致性,而且还要保证大事务执行的一致性。

2.数据库事务和MQ的一致性

数据库事务和MQ的发送是非原子的,上面的代码会出现MQ先执行,DB事务后执行,最终可能会因为异常事务回滚导致不一致的情况。

三.分析

1.问题分析

1.1事务传播性分析

这里使用的事务传播属性是 PROPAGATION_REQUIRED ,如果存在当前事务则用当前事务,如果不存在当前事务,则新建一个事务。根据场景代码,我们可以知道 payOrderPaySuccesstradeOrderPaySuccess 都会使用paymentNotify 的事务,也就是最外层的事务。由此可知,MQ的发送分散在这个大事务提交前的不同时间阶段。

1.2一致性分析

每个小事务单独执行的时候,在完成业务操作后都会添加事件日志,然后再发送MQ,这样能够保证一致性。但是在一个大事务中涉及到原来多个小事务的代码执行时,就会出现不一致性问题,这相当于每一块小事务的代码都会影响到,整个事务的最终提交结果,而MQ的发送是不受事务结果影响的。

2.方案分析

1.1事务和MQ的执行顺序

在不使用事务消息的情况下,我们必须保证先提交事务,后发送MQ消息。这里我们可以把发送MQ的消息规范化,用AOP处理MQ的发送。这里需要注意事务管理和发送MQ两个AOP顺序的调整。

	@SendMqMsg
	@Transactional
	public void payOrderPaySuccess(PayOrderEo payOrderEo){
		// db transaction
		payOrderDas.paySuccess(payOrderEo);
	}

1.2事件日志记录

事件日志记录的作用有两个,一是记录对象操作状态变化流程,二是为MQ的一致性提供补偿记录。这里需要做规范化处理,同样使用AOP,但是这个日志记录要在事务提交前执行,也就是说要与业务代码在同一个事务中。

	@SendMQMsg
	@EventLog
	@Transactional
	public void payOrderPaySuccess(PayOrderEo payOrderEo){
		// db transaction
		payOrderDas.paySuccess(payOrderEo);
	}

AOP解决数据库事务与MQ的问题_第1张图片

1.3大事务的MQ消息发送

为了不影响单个小事务,大事务需要把所有小事务涉及到的MQ消息保存起来,等待数据库事务提交后再一起发送。这里同样可以用AOP处理。在最外一层的大事务方法上做AOP,定义一个线程本地变量,作为各个小事务的MQ消息的收集器。MQ的AOP只作消息的保存操作,而大事务的AOP才做最终的MQ消息发送操作。

    @DelayMQ
	@Transactional
	public void paymentNotify(){
		// 支付单操作
		payOrderService.payOrderPaySuccess();
		// 交易单操作
		tradeOrderService.tradeOrderPaySuccess();
	}

AOP解决数据库事务与MQ的问题_第2张图片

1.4 小结

总结一下AOP各自的职责

1.4.1 DelayMQ

Before:初化本地线程变量-MQ消息列表
After:发送MQ消息列表的消息。

1.4.2 SendMQMsg

After:把MQ消息保存到MQ消息列表

1.4.3 Transactional

spring 的事务管理。

1.4.4 EventLog

After:往数据库插入事件日志。

四.总结

这里只是简单总结了普通MQ消息的发送和数据库事务的处理问题,其它细节问题就不详细说了。最后还留下了一个问题,结合以上方案如何作最少的改动实现事务MQ消息。

你可能感兴趣的:(MQ,aop)