1. 程式人生 > >mysql spring分散式事務處理

mysql spring分散式事務處理

1.XA
XA是由X/Open組織提出的分散式事務的規範。XA規範主要定義了(全域性)事務管理器(Transaction Manager)和(區域性)資源管理器(Resource Manager)之間的介面。XA介面是雙向的系統介面,在事務管理器(Transaction Manager)以及一個或多個資源管理器(Resource Manager)之間形成通訊橋樑。XA之所以需要引入事務管理器是因為,在分散式系統中,從理論上講(參考Fischer等的論文),兩臺機器理論上無法達到一致的狀態,需要引入一個單點進行協調。事務管理器控制著全域性事務,管理事務生命週期,並協調資源。資源管理器負責控制和管理實際資源(如資料庫或JMS佇列)
2.JTA
作為Java平臺上事務規範JTA(Java Transaction API)也定義了對XA事務的支援,實際上,JTA是基於XA架構上建模的,在JTA 中,事務管理器抽象為javax.transaction.TransactionManager介面,並通過底層事務服務(即JTS)實現。像很多其他的java規範一樣,JTA僅僅定義了介面,具體的實現則是由供應商(如J2EE廠商)負責提供,目前JTA的實現主要由以下幾種:


2.1.J2EE容器所提供的JTA實現(JBoss)

2.2.獨立的JTA實現:如JOTM,Atomikos.這些實現可以應用在那些不使用J2EE應用伺服器的環境裡用以提供分佈事事務保證。如Tomcat,Jetty以及普通的java應用。

雖然在Spring中使用Java Transaction API和XA協議進行分散式事務是常見的,但您還有其他選項。最佳實現取決於您的應用程式使用的資源型別以及您願意在效能,安全性,可靠性和資料完整性之間進行權衡.

雖然在Spring中使用Java Transaction API和XA協議進行分散式事務是常見的,但您還有其他選項。最佳實現取決於您的應用程式使用的資源型別以及您願意在效能,安全性,可靠性和資料完整性之間進行權衡。在這個JavaWorld功能中,SpringSource的David Syer引導您在Spring應用程式中分析七種分散式事務模式,其中三種是XA和四種。等級:中級

Spring Framework對Java Transaction API(JTA)的支援使應用程式能夠使用分散式事務和XA協議,而無需在Java EE容器中執行。然而,即使有了這種支援,XA也是昂貴的,管理不方便或麻煩。那麼可能會令人驚喜的是,一定程度的應用程式可以避免使用XA。

為了幫助您瞭解分散式事務的各種方法所涉及的考慮事項,我將分析七個事務處理模式,提供程式碼示例以使其具體化。我將以與安全性或可靠性相反的順序呈現模式,從最普遍的情況下以資料完整性和原子性保證的最高保證開始。當您向下移動列表時,將會使用更多的注意事項和限制。這些模式也大致與執行時間成本相反(從最昂貴的開始)。這些模式都是架構或技術,而不是業務模式,所以我不關注業務用例,只有最小的程式碼才能看到每個模式的工作。

請注意,只有前三種模式涉及XA,並且在效能上可能不可用或可接受。我不像其他人一樣廣泛地討論XA模式,因為它們在其他地方被覆蓋,儘管我提供了第一個模式的簡單演示。通過閱讀本文,您將瞭解分散式事務可以做什麼和不能做什麼,以及如何以及何時避免使用XA - 何時不會。

分散式事務和原子性

一個分散式事務是一個涉及多個事務性資源。事務資源的例子是用於與關係資料庫和訊息中介軟體通訊的聯結器。通常,這樣的資源有看起來像一個API begin()rollback()commit()。在Java世界中,事務資源通常顯示為由底層平臺提供的工廠的產品:對於資料庫,它是Connection(由...生產DataSource)或Java Persistence API(JPA)EntityManager; 對於Java訊息服務(JMS),它是一個Session

在一個典型的例子中,JMS訊息觸發資料庫更新。一個成功的互動分解成一個時間軸,就像這樣:

  1. 啟動訊息交易
  2. 接收訊息
  3. 啟動資料庫事務
  4. 更新資料庫
  5. 提交資料庫事務
  6. 提交訊息交易

如果在更新時發生資料庫錯誤(如約束違規),則所需的順序如下所示:

  1. 啟動訊息交易
  2. 接收訊息
  3. 啟動資料庫事務
  4. 更新資料庫,失敗!
  5. 回滾資料庫事務
  6. 回滾訊息交易

在這種情況下,訊息將在最後一次回滾後返回到中介軟體,並在某一點返回以在另一個事務中接收。這通常是一件好事,因為否則你可能沒有發生故障的記錄。(處理自動重試和處理異常的機制不在本文的範圍之內。)

兩個時間軸的重要特徵是它們是原子的,形成一個完全成功或完全失敗的單個邏輯事務。

但是什麼保證時間線看起來像這些序列之一?事務資源之間的某些同步必須發生,所以如果一個提交它們都做,反之亦然。否則,整個事務不是原子的。事務是分散式的,因為涉及多個資源,沒有同步,它不會是原子的。分散式事務的技術和概念上的困難都與資源的同步(或缺乏)有關。

下面討論的前三種模式是基於XA協議的。因為這些模式已被廣泛覆蓋,我不會在這裡詳細介紹。那些熟悉XA模式的人可能希望跳過共享事務資源模式

全XA與2PC

如果您需要接近防彈保證,您的應用程式的交易將在中斷之後恢復,包括伺服器崩潰,那麼全XA是您唯一的選擇。在這種情況下用於同步事務的共享資源是一個特殊的事務管理器,它使用XA協議來協調關於程序的資訊。在Java中,從開發人員的角度來看,協議是通過JTA公開的UserTransaction

作為一個系統介面,XA是大多數開發人員從未看到的啟用技術。他們需要知道的是,它在那裡,它實現了什麼,它的成本以及它們如何使用事務資源的影響。成本來自於交易管理器使用的兩階段提交(2PC)協議,以確保所有資源在結束之前就事務的結果達成一致。

如果應用程式啟用了Spring,它將使用Spring JtaTransactionManager和Spring宣告式事務管理來隱藏底層同步的詳細資訊。使用XA和不使用XA之間的開發人員的區別在於配置工廠資源:DataSource例項和應用程式的事務管理器。本文包括示例此應用程式(atomikos-db專案)。該DataSource例項和事務管理器是應用程式的唯一XA-或JTA特定的元素。

要檢視樣品的工作,請執行下面的單元測試com.springsource.open.db。一個簡單的MulipleDataSourceTests類只需將資料插入到兩個資料來源中,然後使用Spring整合支援功能來回滾事務,如清單1所示:

清單1.事務回滾

@Transactional@Testpublicvoid testInsertIntoTwoDataSources()throwsException{int count = getJdbcTemplate().update("INSERT into T_FOOS (id,name,foo_date) values (?,?,null)",0,"foo");
    assertEquals(1, count);

    count = getOtherJdbcTemplate().update("INSERT into T_AUDITS (id,operation,name,audit_date) values (?,?,?,?)",0,"INSERT","foo",newDate());
    assertEquals(1, count);// Changes will roll back after this method exits}

然後MulipleDataSourceTests驗證兩個操作是否都回滾,如清單2所示:

清單2.驗證回滾

@AfterTransactionpublicvoid checkPostConditions(){int count = getJdbcTemplate().queryForInt("select count(*) from T_FOOS");// This change was rolled back by the test framework
    assertEquals(0, count);

    count = getOtherJdbcTemplate().queryForInt("select count(*) from T_AUDITS");// This rolled back as well because of the XA
    assertEquals(0, count);}

為了更好地瞭解Spring事務管理的工作原理以及如何配置它,請參見“ Spring參考指南”

XA與1PC優化

該模式是許多事務管理器用於避免2PC的開銷(如果事務包含單個資源)的優化。您期望您的應用程式伺服器能夠了解這一點。

XA和最後資源Gambit

許多XA事務管理器的另一個特點是,當所有資源都具有XA功能時,它們仍然可以提供相同的恢復保證。他們通過排序資源並使用非XA資源作為投票來執行此操作。如果無法提交,那麼所有其他資源都可以回滾。它接近百分之百防彈 - 但不完全是這樣。並且當它失敗時,如果不採取額外的步驟(如在一些頂級實現中所做的那樣),則它失敗而不留下很多痕跡。

共享事務資源模式

在一些系統中降低複雜性和提高吞吐量的一個很好的模式是通過確保系統中的所有事務資源實際上由相同的資源支援來完全消除對XA的需求。這在所有處理用例中顯然是不可能的,但它與XA一樣堅固,通常要快得多。共享事務資源模式是防彈的,但特定於某些平臺和處理場景。

這種模式的一個簡單而熟悉的(很多)示例是在Connection使用物件關係對映(ORM)與使用JDBC的元件之間共享資料庫。這就是您使用支援ORM工具(如HibernateEclipseLinkJava Persistence API(JPA))的Spring事務管理器。相同的事務可以安全地在ORM和JDBC元件之間使用,通常通過由事務控制的服務級方法執行從上面驅動。

這種模式的另一個有效用途是單個數據庫的訊息驅動更新的情況(如本文的簡介中的簡單示例)。訊息中介軟體系統需要將資料儲存在某處,通常在關係資料庫中。為了實現這種模式,所有需要的是將訊息系統指向業務資料所在的同一資料庫。此模式依賴於訊息中介軟體供應商暴露其儲存策略的細節,以便可以將其配置為指向相同的資料庫並掛接到同一事務中。

並不是所有的供應商都使這個很容 幾乎任何資料庫的替代方案是使用Apache ActiveMQ進行訊息傳遞,並將儲存策略插入到訊息代理中。一旦你知道這個技巧,這是很容易配置的。這在本文的shared-jms-db示例專案中得到證明。應用程式程式碼(在這種情況下為單元測試)不需要意識到該模式正在使用,因為它在Spring配置中都以宣告方式啟用。

在示例中的單元測試SynchronousMessageTriggerAndRollbackTests驗證一切正在使用同步訊息接收。該testReceiveMessageUpdateDatabase方法接收兩條訊息,並使用它們在資料庫中插入兩條記錄。當此方法退出時,測試框架回滾事務,因此您可以驗證訊息和資料庫更新是否都回滾,如清單3所示:

清單3.驗證訊息和資料庫更新的回滾

@AfterTransactionpublicvoid checkPostConditions(){

  assertEquals(0,SimpleJdbcTestUtils.countRowsInTable(jdbcTemplate,"T_FOOS"));List<String> list = getMessages();
  assertEquals(2, list.size());}

配置的最重要的功能是ActiveMQ永續性策略,將訊息傳遞系統DataSource與業務資料相同,Spring上的標誌JmsTemplate用於接收訊息。清單4顯示瞭如何配置ActiveMQ永續性策略:

清單4.配置ActiveMQ永續性

<beanid="connectionFactory"class="org.apache.activemq.ActiveMQConnectionFactory"depends-on="brokerService"><propertyname="brokerURL"value="vm://localhost?async=false"/></bean><beanid="brokerService"class="org.apache.activemq.broker.BrokerService"init-method="start"destroy-method="stop">
    ...
  <propertyname="persistenceAdapter"><beanclass="org.apache.activemq.store.jdbc.JDBCPersistenceAdapter"><propertyname="dataSource"><beanclass="com.springsource.open.jms.JmsTransactionAwareDataSourceProxy"><propertyname="targetDataSource"ref="dataSource"/><propertyname="jmsTemplate"ref="jmsTemplate"/></bean></property><propertyname="createTablesOnStartup"value="true"/></bean></property></bean>

清單5顯示了Spring JmsTemplate上用於接收訊息的標誌:

清單5.設定JmsTemplate事務性使用

<beanid="jmsTemplate"class="org.springframework.jms.core.JmsTemplate">
  ...
  <!-- This is important... --><propertyname="sessionTransacted"value="true"/></bean>

沒有sessionTransacted=true,將永遠不會進行JMS會話事務API呼叫,並且不能回滾訊息接收。這裡的重要組成部分是具有特殊async=false引數和包裝器的嵌入式代理,DataSource它們一起確保ActiveMQ Connection與Spring 使用相同的事務性JDBC 。

共享資料庫資源有時可以從現有的獨立資源合成,特別是如果它們都在相同的RDBMS平臺中。企業級資料庫供應商都支援同義詞(或等效)的概念,其中一個模式中的表(使用Oracle術語)在另一個模式中被宣告為同義詞。以這種方式,在平臺中物理分配的資料可以Connection在JDBC客戶端中從事務處理事務處理。例如,在實際系統(與樣本相反)中使用ActiveMQ實現共享資源模式通常涉及為訊息傳遞和業務資料建立同義詞。

效能和JDBCPersistenceAdapter

ActiveMQ社群的一些人聲稱JDBCPersistenceAdapter建立效能問題。然而,許多專案和實時系統使用ActiveMQ與關係資料庫。在這些情況下,收到的智慧是使用介面卡的日記版本來提高效能。這不符合共享資源模式(因為日誌本身是一個新的事務資源)。然而,陪審團可能仍然存在JDBCPersistenceAdapter.。實際上,有理由認為使用共享資源可能會提高效能超過期刊的情況。這是Spring和ActiveMQ工程團隊積極研究的一個領域。

在非訊息場景(多個數據庫)中的另一種共享資源技術是使用Oracle資料庫連結功能將RDBMS平臺級的兩個資料庫模式連結在一起(參見參考資料)。這可能需要更改應用程式程式碼或建立同義詞,因為引用連結資料庫的表名稱別名包括連結的名稱。

最佳努力1PC模式

最好的努力1PC模式是相當普遍的,但在某些情況下可能會失敗,開發人員必須注意。這是一種非XA模式,涉及許多資源的同步單階段提交。因為沒有使用2PC,所以它永遠不會像XA交易一樣安全,但是如果參與者意識到妥協,通常就夠了。許多高容量,高吞吐量的事務處理系統被設定成這樣來提高效能。

基本思想是在事務中儘可能延遲所有資源的提交,從而唯一可能出錯的是基礎架構故障(而不是業務處理錯誤)。依靠最佳努力的系統1PC的原因是基礎設施故障很少,以至於他們能承受風險以換取更高的吞吐量。如果商業處理服務也被設計為冪等,那麼在實踐中幾乎不會出錯。

為了幫助您更好地瞭解模式並分析故障的後果,我將以訊息驅動的資料庫更新為例。

這筆交易中的兩個資源被計入和計數。訊息事務在資料庫之前啟動,它們以相反的順序結束(提交或回滾)。所以在成功的情況下的順序可能與本文開頭的相同:

  1. 啟動訊息交易
  2. 接收訊息
  3. 啟動資料庫事務
  4. 更新資料庫
  5. 提交資料庫事務
  6. 提交訊息交易

實際上,前四個步驟的順序並不重要,除了必須在更新資料庫之前收到訊息,並且每個事務必須在使用相應的資源之前開始。所以這個順序是一樣的:

  1. 啟動訊息交易
  2. 啟動資料庫事務
  3. 接收訊息
  4. 更新資料庫
  5. 提交資料庫事務
  6. 提交訊息交易

關鍵在於最後兩個步驟很重要:他們必須按照這個順序來到最後。訂購重要的原因是技術性的,但訂單本身是由業務需求決定的。該命令告訴您,這種情況下的一種事務資源是特殊的; 它包含有關如何執行另一項工作的說明。這是一個業務訂購:系統無法自動告知哪種方式(儘管如果訊息和資料庫是兩個資源,那麼它通常是這樣的)。排序的重要原因與故障案例有關。最常見的故障案例(到目前為止)是業務處理失敗(不良資料,程式設計錯誤等)。在這種情況下,這兩個事務都可以輕鬆地被繫結以響應異常和回滾。

觸發回滾的精確機制是不重要的; 有幾個可用。重要的是,提交或回滾按照資源中的業務排序的相反順序進行。在示例應用程式中,訊息傳遞事務必須最後提交,因為該資源中包含業務流程的指令。這是重要的,因為(罕見)失敗的情況下,第一個提交成功,第二個失敗。由於設計上所有的業務處理都已經完成,所以這種部分失敗的唯一原因將是訊息中介軟體的基礎設施問題。

請注意,如果資料庫資源的提交失敗,則淨效果仍然是回滾。所以唯一的非原子故障模式是第一個事務提交併且第二個回滾的模式。更一般地說,如果n事務中有資源,則有n-1這樣的故障模式,使一些資源在回滾之後處於不一致(提交)狀態。在訊息資料庫用例中,此失敗模式的結果是訊息回滾並返回到另一個事務中,即使已經成功處理。所以你可以放心地認為,可能發生的最糟糕的事情是可以傳遞重複的訊息。在更一般的情況下,

有些人承擔重複郵件的發生頻率不足的風險,他們不會試圖預期他們。為了更有信心您的業務資料的正確性和一致性,您需要在業務邏輯中瞭解它們。如果業務處理意識到重複的訊息可能到達,那麼所有它必須做的(通常以一些額外的成本,但不如2PC)要檢查它是否已經處理過該資料,如果有的話,則不做任何操作。這種專業化有時被稱為冪等業務服務模式。

示例程式碼包括使用此模式同步事務資源的兩個示例。我會依次討論,然後再考察一些其他的選擇。

春天和訊息驅動的POJO

示例程式碼best-jms-db project,參與者被設定成使用主流的配置選項,使得盡力而為1PC圖案之後。這個想法是傳送到佇列的訊息被非同步偵聽器拾取,並用於將資料插入到資料庫中的表中。

TransactionAwareConnectionFactoryProxy-股票元件在Spring設計在這個模式中使用-是關鍵因素。而不是使用提供的原始供應商ConnectionFactory,配置包裝ConnectionFactory在處理事務同步的裝飾器中。這jms-context.xml,在清單6中顯示:

清單6.配置一個TransactionAwareConnectionFactoryProxy包裝供應商提供的JMSConnectionFactory

<beanid="connectionFactory"class="org.springframework.jms.connection.TransactionAwareConnectionFactoryProxy"><propertyname="targetConnectionFactory"><beanclass="org.apache.activemq.ActiveMQConnectionFactory"depends-on="brokerService"><propertyname="brokerURL"value="vm://localhost"/></bean></property><propertyname="synchedLocalTransactionAllowed"value="true"/></bean>

不需要ConnectionFactory知道哪個事務管理器進行同步,因為在需要時只有一個事務處於活動狀態,Spring可以在內部處理。駕駛交易由正常DataSourceTransactionManager配置進行處理data-source-context.xml。需要了解事務管理器的元件是將輪詢和接收訊息的JMS偵聽器容器:

<jms:listener-containertransaction-manager="transactionManager"><jms:listenerdestination="async"ref="fooHandler"method="handle"/></jms:listener-container>

fooHandlermethod告訴聽者容器當訊息的“非同步”到達佇列呼叫哪個方法,其元件上。處理程式是這樣實現的,接受String傳入的訊息,並使用它來插入記錄:

publicvoid handle(String msg){

  jdbcTemplate.update("INSERT INTO T_FOOS (ID, name, foo_date) values (?, ?,?)", count.getAndIncrement(), msg,newDate());}

為了模擬故障,程式碼使用一個FailureSimulator方面。它檢查訊息內容,看看它是否應該失敗,並以什麼方式。maybeFail()清單7所示的方法在FooHandler處理訊息之後呼叫,但在事務結束之前呼叫,以便它可以影響事務的結果:

清單7. maybeFail()方法

@AfterReturning("execution(* *..*Handler+.handle(String)) && args(msg)")publicvoid maybeFail(String msg){if(msg.contains("fail")){if(msg.contains("partial")){
      simulateMessageSystemFailure();}else{
      simulateBusinessProcessingFailure();}}}

simulateBusinessProcessingFailure()方法只是丟擲一個DataAccessException資料庫訪問失敗的方式。觸發此方法時,您將期望所有資料庫和訊息事務的完全回滾。在示例專案的AsynchronousMessageTriggerAndRollbackTests單元測試中測試了這種情況。

simulateMessageSystemFailure()方法通過癱瘓基礎JMS來模擬訊息系統中的故障Session。這裡的預期結果是部分提交:資料庫工作保持提交,但訊息回滾。這在AsynchronousMessageTriggerAndPartialRollbackTests單元測試中進行了測試。

樣本包還包括在AsynchronousMessageTriggerSunnyDayTests課堂上成功交付所有交易工作的單元測試。

相同的JMS配置和相同的業務邏輯也可以用於同步設定,其中訊息在業務邏輯中的阻塞呼叫中接收,而不是委派給偵聽器容器。這種方法也在best-jms-db示例專案中得到體現。在晴天的情況下和全回滾在測試SynchronousMessageTriggerSunnyDayTestsSynchronousMessageTriggerAndRollbackTests分別。

連結交易經理

在最佳努力1PC模式(best-db-db專案)的另一個示例中,事務管理器的粗略實現只是將其他事務管理器的列表連結在一起以實現事務同步。如果業務處理成功,他們都會承諾,如果不是,他們都會回滾。

實現在ChainedTransactionManager其中接受其他事務管理器作為注入屬性的列表,如清單8所示:

清單8. ChainedTransactionManager的配置

<beanid="transactionManager"class="com.springsource.open.db.ChainedTransactionManager"><propertyname="transactionManagers"><list><beanclass="org.springframework.jdbc.datasource.DataSourceTransactionManager"><propertyname="dataSource"ref="dataSource"/></bean><beanclass="org.springframework.jdbc.datasource.DataSourceTransactionManager"><propertyname="dataSource"ref="otherDataSource"/></bean></list></property></bean>

此配置的最簡單的測試只是在資料庫中插入某些東西,回滾並檢查兩個操作都沒有跟蹤。這MulipleDataSourceTests與XA示例atomikos-db專案中的單元測試一樣實現。如果回滾未同步但提交發生了工作,則測試失敗。

請記住,資源的順序很重要。它們是巢狀的,並且提交或回滾以與它們被登記的順序相反的順序發生(這是配置中的順序)。這使得其中一個資源是特殊的:如果有問題,最外層的資源總是回滾,即使唯一的問題是該資源的故障。此外,testInsertWithCheckForDuplicates()測試方法顯示了一種保護系統不受部分故障影響的業務流程。在內部資源(otherDataSource在這種情況下)的業務運營中被實施為防禦性檢查:

int count = otherJdbcTemplate.