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 ,如果存在當前事務則用當前事務,如果不存在當前事務,則新建一個事務。根據場景代碼,我們可以知道 payOrderPaySuccess 和 tradeOrderPaySuccess 都會使用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消息。