Spring事務傳播行爲實戰

Spring事務傳播行爲實戰

概述

Spring框架提供了事務管理的標準實現,且能夠經過註解或者XML文件的方式聲明和配置事務。前端

經過異步事件的方式解耦服務調用,能夠提升程序的響應速度,而且避免由於事務傳播行爲而致使的事務問題。java

本文以一個電商平臺包裹出庫的業務爲實際背景,經過異步事件與線程池的方式解耦嵌套事務,提升程序併發性能;爲了便於問題的分析和方案的理解,同時還講解了Spring的事務管理,並着重介紹了幾種不一樣的事務傳播行爲。spring

事務小貼士

什麼是事務呢?簡單來說事務就是邏輯上的一組操做,這些操做要麼都執行,要麼都不執行。數據庫

什麼是事務管理呢?所謂事務管理,其實就是「按照給定的事務規則來執行事務的提交或者回滾操做」。多線程

事務的機制實現很大一部分依賴事務日誌文件(undo log和redo log)。事務日誌是一個與數據庫文件分開的文件。它存儲對數據庫進行的全部更改,並所有記錄插入、更新、刪除、提交、回退和數據庫模式變化。事務日誌是備份和恢復的重要組件。併發

訂單出庫失敗

在訂單的包裹出庫以前,會將出庫指令下發到商城,其中包含包裹寄送的快遞信息,以便銷售平臺展現快遞跟蹤信息;同時,爲了防止前端由於超賣或者重複下單等緣由,已經將訂單取消,會根據前端商城的返回狀態判斷訂單是否能夠出庫。框架

其中,系統交互流程以下,在前端銷售平臺與WMS(倉儲管理系統)之間,會有統一的OMS(訂單管理系統)進行銷售單的管理和數據中轉。異步

當前端銷售平臺收到出庫信息之後,會進行以下的校驗與操做:分佈式

爲了防止調用通知服務的時候出現異常,影響包裹出庫,調用通知服務的代碼是用try-catch語句包裹起來的。post

可是,某些訂單在出庫的時候,仍是出現了以下異常,出庫失敗:

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)
複製代碼

根據事務的傳播行爲,Transaction rolled back because it has been marked as rollback-only應該是由於被調用的事務回滾了,當調用一方提交事務的時候由於被標記爲了rollback-only,所以沒法正常提交。

Spring事務的傳播機制

下面咱們詳細聊一下Spring事務的傳播機制。

Spring的@Transactional註解能夠用於聲明方法支持事務,Spring底層經過AOP的方式實現事務的控制。

@Transactional註解中的Propagation屬性能夠用於指定事務的傳播行爲。

/** * 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: 建立一個新的事務,若是當前存在事務,則把當前事務掛起。不支持當前事務。

  • TransactionDefinition.PROPAGATION_NOT_SUPPORTED: 以非事務方式運行,若是當前存在事務,則把當前事務掛起。不支持當前事務。

  • TransactionDefinition.PROPAGATION_NEVER:

以非事務方式運行,若是當前存在事務,則拋出異常。不支持當前事務。

  • TransactionDefinition.PROPAGATION_REQUIRED: 若是當前存在事務,則加入該事務;若是當前沒有事務,則建立一個新的事務。支持當前事務。

  • TransactionDefinition.PROPAGATION_SUPPORTS: 若是當前存在事務,則加入該事務;若是當前沒有事務,則以非事務的方式繼續運行。支持當前事務。

  • TransactionDefinition.PROPAGATION_MANDATORY: 若是當前存在事務,則加入該事務;若是當前沒有事務,則拋出異常。支持當前事務。

  • TransactionDefinition.PROPAGATION_NESTED: 若是當前存在事務,則建立一個事務做爲當前事務的嵌套事務來運行;若是當前沒有事務,則該取值等價於TransactionDefinition.PROPAGATION_REQUIRED。支持當前事務。

這裏須要指出的是,前面的六種事務傳播行爲是 Spring 從 EJB 中引入的,他們共享相同的概念。而 PROPAGATION_NESTED 是 Spring 所特有的。以 PROPAGATION_NESTED 啓動的事務內嵌於外部事務中(若是存在外部事務的話),此時,內嵌事務並非一個獨立的事務,它依賴於外部事務的存在,只有經過外部的事務提交,才能引發內部事務的提交,嵌套的子事務不能單獨提交。若是熟悉 JDBC 中的保存點(SavePoint)的概念,那嵌套事務就很容易理解了,其實嵌套的子事務就是保存點的一個應用,一個事務中能夠包括多個保存點,每個嵌套子事務。另外,外部事務的回滾也會致使嵌套子事務的回滾。

利用事務傳播行爲的解決方案

基於上面事務傳播行爲的講解,能夠將事務的傳播行爲設置爲PROPAGATION_REQUIRES_NEW,這樣當前事務與被調用事務將是兩個不一樣的事務,能夠分別提交或者回滾。

在外層事務捕獲異常之後執行以下代碼,會輸出false,說明內層事務回滾並未傳播給外層事務: TransactionAspectSupport.currentTransactionStatus().isRollbackOnly()

而在內層事務執行以下代碼,會返回true,說明內層事務是一個新的事務,在執行改事務的時候,當前事務會被掛起: TransactionAspectSupport.currentTransactionStatus().isNewTransaction()

這樣就解決了try-catch塊拋出異常因事務傳播行爲致使的當前事務沒法提交的問題。

利用多線程的解決方案

咱們知道,在不一樣的線程之間,事務是獨立的。對於發送通知消息這樣的業務,適合經過拋出事件的方式,而後經過異步監聽器進行處理。

其流程以下:

至於事件模型的實現方式,不管是分佈式的Zookeeper、Redis和MQ,仍是非分佈式的Guava Event Bus、Spring Event均可以,能夠根據實際須要選擇。其核心原理仍然是發佈訂閱模式。

參考連接

數據庫事務的方方面面

多是最漂亮的Spring事務管理詳解

Transaction必知必會


Wechat-westcall
相關文章
相關標籤/搜索