淺談Spring事務(wù)傳播行為實戰(zhàn)
Spring框架提供了事務(wù)管理的標準實現(xiàn),且可以通過注解或者XML文件的方式聲明和配置事務(wù)。
通過異步事件的方式解耦服務(wù)調(diào)用,可以提高程序的響應(yīng)速度,并且避免因為事務(wù)傳播行為而導(dǎo)致的事務(wù)問題。
本文以一個電商平臺包裹出庫的業(yè)務(wù)為實際背景,通過異步事件與線程池的方式解耦嵌套事務(wù),提高程序并發(fā)性能;為了便于問題的分析和方案的理解,同時還講解了Spring的事務(wù)管理,并著重介紹了幾種不同的事務(wù)傳播行為。
事務(wù)小貼士
什么是事務(wù)呢?簡單來講事務(wù)就是邏輯上的一組操作,這些操作要么都執(zhí)行,要么都不執(zhí)行。
什么是事務(wù)管理呢?所謂事務(wù)管理,其實就是“按照給定的事務(wù)規(guī)則來執(zhí)行事務(wù)的提交或者回滾操作”。
事務(wù)的機制實現(xiàn)很大一部依賴事務(wù)日志文件(undo log和redo log)。事務(wù)日志是一個與數(shù)據(jù)庫文件分開的文件。它存儲對數(shù)據(jù)庫進行的所有更改,并全部記錄插入、更新、刪除、提交、回退和數(shù)據(jù)庫模式變化。事務(wù)日志是備份和恢復(fù)的重要組件。
訂單出庫失敗
在訂單的包裹出庫之前,會將出庫指令下發(fā)到商城,其中包含包裹寄送的快遞信息,以便銷售平臺展示快遞跟蹤信息;同時,為了防止前端因為超賣或者重復(fù)下單等原因,已經(jīng)將訂單取消,會根據(jù)前端商城的返回狀態(tài)判斷訂單是否可以出庫。
其中,系統(tǒng)交互流程如下,在前端銷售平臺與WMS(倉儲管理系統(tǒng))之間,會有統(tǒng)一的OMS(訂單管理系統(tǒng))進行銷售單的管理和數(shù)據(jù)中轉(zhuǎn)。
當(dāng)前端銷售平臺收到出庫信息以后,會進行如下的校驗與操作:
為了防止調(diào)用通知服務(wù)的時候出現(xiàn)異常,影響包裹出庫,調(diào)用通知服務(wù)的代碼是用try-catch語句包裹起來的。
但是,某些訂單在出庫的時候,還是出現(xiàn)了如下異常,出庫失敗:
org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only at org.springframework.transaction.support.AbstractPlatformTransactionManager.processRollback(AbstractPlatformTransactionManager.java:873) at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:710) at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:534) at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:305) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688)
根據(jù)事務(wù)的傳播行為, Transaction rolled back because it has been marked as rollback-only
應(yīng)該是因為被調(diào)用的事務(wù)回滾了,當(dāng)調(diào)用一方提交事務(wù)的時候因為被標記為了 rollback-only ,因此無法正常提交。
Spring事務(wù)的傳播機制
下面我們詳細聊一下Spring事務(wù)的傳播機制。
Spring的 @Transactional
注解可以用于聲明方法支持事務(wù),Spring底層通過AOP的方式實現(xiàn)事務(wù)的控制。
@Transactional
注解中的 Propagation
屬性可以用于指定事務(wù)的傳播行為。
/** * The transaction propagation type. * <p>Defaults to {@link Propagation#REQUIRED}. * @see org.springframework.transaction.interceptor.TransactionAttribute#getPropagationBehavior() */ Propagation propagation() default Propagation.REQUIRED;
在TransactionDefinition定義中包括了如下幾個表示傳播行為的常量:
- TransactionDefinition.PROPAGATION_REQUIRES_NEW: 創(chuàng)建一個新的事務(wù),如果當(dāng)前存在事務(wù),則把當(dāng)前事務(wù)掛起。不支持當(dāng)前事務(wù)。
- TransactionDefinition.PROPAGATION_NOT_SUPPORTED: 以非事務(wù)方式運行,如果當(dāng)前存在事務(wù),則把當(dāng)前事務(wù)掛起。不支持當(dāng)前事務(wù)。
- TransactionDefinition.PROPAGATION_NEVER:
以非事務(wù)方式運行,如果當(dāng)前存在事務(wù),則拋出異常。不支持當(dāng)前事務(wù)。
- TransactionDefinition.PROPAGATION_REQUIRED: 如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前沒有事務(wù),則創(chuàng)建一個新的事務(wù)。支持當(dāng)前事務(wù)。
- TransactionDefinition.PROPAGATION_SUPPORTS: 如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前沒有事務(wù),則以非事務(wù)的方式繼續(xù)運行。支持當(dāng)前事務(wù)。
- TransactionDefinition.PROPAGATION_MANDATORY: 如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前沒有事務(wù),則拋出異常。支持當(dāng)前事務(wù)。
- TransactionDefinition.PROPAGATION_NESTED: 如果當(dāng)前存在事務(wù),則創(chuàng)建一個事務(wù)作為當(dāng)前事務(wù)的嵌套事務(wù)來運行;如果當(dāng)前沒有事務(wù),則該取值等價于TransactionDefinition.PROPAGATION_REQUIRED。支持當(dāng)前事務(wù)。
這里需要指出的是,前面的六種事務(wù)傳播行為是 Spring 從 EJB 中引入的,他們共享相同的概念。而 PROPAGATION_NESTED 是 Spring 所特有的。以 PROPAGATION_NESTED 啟動的事務(wù)內(nèi)嵌于外部事務(wù)中(如果存在外部事務(wù)的話),此時,內(nèi)嵌事務(wù)并不是一個獨立的事務(wù),它依賴于外部事務(wù)的存在,只有通過外部的事務(wù)提交,才能引起內(nèi)部事務(wù)的提交,嵌套的子事務(wù)不能單獨提交。如果熟悉 JDBC 中的保存點(SavePoint)的概念,那嵌套事務(wù)就很容易理解了,其實嵌套的子事務(wù)就是保存點的一個應(yīng)用,一個事務(wù)中可以包括多個保存點,每一個嵌套子事務(wù)。另外,外部事務(wù)的回滾也會導(dǎo)致嵌套子事務(wù)的回滾。
利用事務(wù)傳播行為的解決方案
基于上面事務(wù)傳播行為的講解,可以將事務(wù)的傳播行為設(shè)置為 PROPAGATION_REQUIRES_NEW
,這樣當(dāng)前事務(wù)與被調(diào)用事務(wù)將是兩個不同的事務(wù),可以分別提交或者回滾。
在外層事務(wù)捕獲異常以后執(zhí)行如下代碼,會輸出 false
,說明內(nèi)層事務(wù)回滾并未傳播給外層事務(wù): TransactionAspectSupport.currentTransactionStatus().isRollbackOnly()
而在內(nèi)層事務(wù)執(zhí)行如下代碼,會返回 true
,說明內(nèi)層事務(wù)是一個新的事務(wù),在執(zhí)行改事務(wù)的時候,當(dāng)前事務(wù)會被掛起: TransactionAspectSupport.currentTransactionStatus().isNewTransaction()
這樣就解決了try-catch塊拋出異常因事務(wù)傳播行為導(dǎo)致的當(dāng)前事務(wù)無法提交的問題。
利用多線程的解決方案
我們知道,在不同的線程之間,事務(wù)是獨立的。對于發(fā)送通知消息這樣的業(yè)務(wù),適合通過拋出事件的方式,然后通過異步監(jiān)聽器進行處理。
其流程如下:
至于事件模型的實現(xiàn)方式,無論是分布式的Zookeeper、Redis和MQ,還是非分布式的Guava Event Bus、Spring Event都可以,可以根據(jù)實際需要選擇。其核心原理仍然是發(fā)布訂閱模式。
參考鏈接
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Java連接SAP RFC實現(xiàn)數(shù)據(jù)抽取的示例詳解
這篇文章主要為大家學(xué)習(xí)介紹了Java如何連接SAP RFC實現(xiàn)數(shù)據(jù)抽取的功能,文中的示例代碼講解詳細,具有一定的參考價值,需要的可以了解下2023-08-08經(jīng)典再現(xiàn) 基于JAVA平臺開發(fā)坦克大戰(zhàn)游戲
經(jīng)典再現(xiàn),這篇文章主要介紹了基于JAVA平臺開發(fā)坦克大戰(zhàn)游戲的相關(guān)代碼,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-06-06IDEA連接postgressql數(shù)據(jù)庫操作
這篇文章主要介紹了IDEA連接postgressql數(shù)據(jù)庫操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-08-08