AOP解決數據庫事務與MQ的問題

一.場景

項目中複雜的業務流程通常由簡單的多個事務組成,每個簡單的事務都可能涉及到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);
	}

在這裏插入圖片描述

1.3大事務的MQ消息發送

爲了不影響單個小事務,大事務需要把所有小事務涉及到的MQ消息保存起來,等待數據庫事務提交後再一起發送。這裏同樣可以用AOP處理。在最外一層的大事務方法上做AOP,定義一個線程本地變量,作爲各個小事務的MQ消息的收集器。MQ的AOP只作消息的保存操作,而大事務的AOP才做最終的MQ消息發送操作。

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

在這裏插入圖片描述

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消息。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章