久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

如何實(shí)現(xiàn)XA式,、非XA式Spring分布式事務(wù)

 feimishiwo 2015-06-17
本文由 ImportNew - 喬永琪 翻譯自 javaworld,。歡迎加入翻譯小組。轉(zhuǎn)載請(qǐng)見文末要求,。

Spring應(yīng)用的幾種事務(wù)處理機(jī)制

Java Transaction API和XA協(xié)議是Spring常用的分布式事務(wù)機(jī)制,,不過你可以選擇選擇其他的實(shí)現(xiàn)方式,。理想的實(shí)現(xiàn)取決于你的應(yīng)用程序使用何種資源,,你愿意在性能、安全,、系統(tǒng)穩(wěn)健性,、數(shù)據(jù)完整方面做出何種權(quán)衡。在這次JavaWorld大會(huì)上,,來自SpringSource的David Syer跟大家分享了Spring應(yīng)用的幾種事務(wù)處理機(jī)制,、三種XA式,、四種非XA式事務(wù)協(xié)議。

Spring框架支持Java Transaction API(JTA),,這樣應(yīng)用就可以脫離Java EE容器,,轉(zhuǎn)而利用分布式事務(wù)以及XA協(xié)議。然而即使有這樣的支持,,XA開銷是昂貴的,,不穩(wěn)定而且笨重不利于管理,不過一些其他的應(yīng)用可以避免使用XA協(xié)議,。

為了讓大家對(duì)所涉及的幾種分布式事務(wù)有所了解,,我會(huì)分析七種事務(wù)處理模式,并 給出具體代碼實(shí)現(xiàn),。并且從安全或者穩(wěn)定性入手倒序展示,,可以看看從安全、穩(wěn)定性出發(fā),,如何在一般場(chǎng)景下,,保障數(shù)據(jù)高完整性和原子性。當(dāng)然隨著話題的深入,, 更多的說明以及限制就會(huì)出現(xiàn),。模式也可以從運(yùn)行時(shí)開銷倒序展示??紤]到所有模式都是結(jié)構(gòu)化或者學(xué)術(shù)性的,,這一點(diǎn)有別于業(yè)務(wù)模型,因此我不打算展開業(yè)務(wù)用例 分析,,僅僅關(guān)注每種模式其少部分代碼如何工作的,。

盡管只有起初的三種模式涉及到 XA協(xié)議,不過從性能角度出發(fā),,這些模式或許無法滿足需求,。考慮到這些模式無處不在,,我不想做過多地?cái)U(kuò)展,,只是對(duì)第一種模式做一個(gè)簡(jiǎn)單的展示。讀完此文,,你可以了解可以用分布式事務(wù)做些什么,、不能做什么以及如何、何時(shí)避免使用XA,,何時(shí)必須使用,。

分布式事務(wù)以及原子性

分布式事務(wù)涉及不止一個(gè)事務(wù)資源。比如,,在關(guān)系數(shù)據(jù)庫(kù)和消息中間件之間通信的連接器,,通常這些資源擁有類似begin(),、rollback()、commit()的API,。在此,,一個(gè)事務(wù)資源通常是一個(gè)工廠產(chǎn)品,這個(gè)工廠通常由底層平臺(tái)提供:以數(shù)據(jù)庫(kù)為例,,DataSource提供Connection,,或者Java Persistence API(JPA)的EntityManager接口;又如Java Message Service(JMS)提供的Session,。

一個(gè)典型的例子,,一個(gè)JMS消息觸發(fā)一次數(shù)據(jù)庫(kù)更新。此過程可以分解成一時(shí)間線,,一個(gè)成功的交互順序是下面這樣:

  1. 開啟消息事務(wù)
  2. 接受消息
  3. 開啟數(shù)據(jù)庫(kù)事務(wù)
  4. 更新數(shù)據(jù)庫(kù)
  5. 提交數(shù)據(jù)庫(kù)事務(wù)
  6. 提交消息事務(wù)

如果數(shù)據(jù)庫(kù)出錯(cuò),,比如更新時(shí)出現(xiàn)諸如違反約束的問題,一個(gè)理想的順序應(yīng)該是下面這個(gè)樣子:

  1. 開啟消息事務(wù)
  2. 接受消息
  3. 開啟數(shù)據(jù)庫(kù)事務(wù)
  4. 更新數(shù)據(jù)庫(kù)失敗
  5. 回滾數(shù)據(jù)庫(kù)事務(wù)
  6. 回滾消息事務(wù)

在這個(gè)案例中,,最后的回滾發(fā)生后消息返回給中間件,,并且在某種程度返回的消息會(huì)被其他事務(wù)所接收。通常這是件好事,,可能你并沒有對(duì)失敗做記錄,。自動(dòng)重試處理異常機(jī)制超出了本文的范疇。

以上兩種時(shí)間線中最重要的特性是它們的原子性,,形成一個(gè)單一的邏輯事務(wù)單元,,要么都成功要么都失敗。

那么用什么確保時(shí)間線會(huì)的順序呢,?事務(wù)資源之間必須保持某種同步,,一旦對(duì)某個(gè)數(shù)據(jù)源做提交,要么都提交了,,要么都回滾,。否者整個(gè)事務(wù)就不缺乏原子性。之所以是分布式事務(wù),,是因?yàn)橛卸鄠€(gè)數(shù)據(jù)源,,沒有同步就沒有原子性。分布式事務(wù)技術(shù)和概念的核心問題都是圍繞資源的同步或者無法同步展開的,。

前三種模式的以下討論都是基于XA協(xié)議,,考慮到這三種模式分布廣泛,本文不會(huì)涉及太多的細(xì)節(jié),,倘若你熟悉XA模式或許愿意直接跳到共享事務(wù)資源模式,。

二階段提交完整XA協(xié)議

如果你需要近乎完美的防護(hù) (close-to-bulletproof)確保你的應(yīng)用事務(wù)在斷電后恢復(fù)以及服務(wù)器崩潰,,完整XA是不二之選,。共享資源通常需要做事務(wù)同步,,在此情況下,它是一個(gè)采用XA協(xié)議協(xié)調(diào)處理過程的信息特殊的事務(wù)管理器,。在Java領(lǐng)域,,從開發(fā)者的角度看,這個(gè)協(xié)議是通過JPA UserTransaction暴露給大家,。

基于系統(tǒng)接口,,XA作為一種促成科技(enabling technology)對(duì)多數(shù)開發(fā)人員不可見,因此他們需要知道XA在哪,、促成什么,、耗損如何以及如何利用事務(wù)資源。事務(wù)管理器采用二階段提交(2PC)協(xié)議,,在確保事務(wù)結(jié)束前所有資源采用同一個(gè)事務(wù)結(jié)果的同時(shí),,也會(huì)帶來性能耗損。

如 果是Spring促成的(Spring-enabled),,應(yīng)用會(huì)采用Spring的JtaTransactionManager以及Spring聲明式 事務(wù)管理,,這樣會(huì)隱藏到了底層事務(wù)同步的具體細(xì)節(jié)。對(duì)于開發(fā)人員用沒用XA的差別就在于對(duì)工廠資源的配置:DataSource實(shí)例,,以及應(yīng)用的事務(wù)管理 器,。本文會(huì)通過一個(gè)應(yīng)用案例(atomikos-db項(xiàng)目)來揭示這個(gè)配置,數(shù)據(jù)庫(kù)實(shí)例和事務(wù)管理器僅是XA或者JTA特定的應(yīng)用元素,。

為了揭示此案例如何工作,,在com.springsource.open.db.下運(yùn)行這個(gè)單元測(cè)試。一個(gè)簡(jiǎn)單的 MulipleDataSourceTests類僅是將數(shù)據(jù)插入兩個(gè)數(shù)據(jù)源中,,并且采用Spring整合支持的特性對(duì)事務(wù)進(jìn)行回滾,,代碼見清單1:

清單1、事務(wù)回滾

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Transactional
  @Test
  public void testInsertIntoTwoDataSources() throws Exception {
    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", new Date());
    assertEquals(1, count);
    // Changes will roll back after this method exits
  }

接著驗(yàn)證這兩個(gè)操作是否同時(shí)回滾,,代碼清單如清單2:

清單2,、回滾驗(yàn)證

1
2
3
4
5
6
7
8
9
10
11
12
@AfterTransaction
  public void 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);
  }

更進(jìn)一步理解Spring事務(wù)管理如何工作以及如何配置,請(qǐng)參看Spring參考指南,。

一階段提交優(yōu)化XA協(xié)議

許多事務(wù)管理器采用這種優(yōu)化模式,,可以避免單一事務(wù)資源下的2PC過度開銷,你的應(yīng)用服務(wù)器最好能夠判別此種情況,。

協(xié)議和最終資源策略

多數(shù)XA事務(wù)管理器另一個(gè)特性是,,不論是單一XA兼 容資源還是所有資源都XA兼容,事務(wù)管理器均能提供相同的恢復(fù)保障,。它們是通過給資源排序,,并且給非XA資源投票實(shí)現(xiàn),倘若事務(wù)提交失敗,,所有其他的資源 都能回滾,。事務(wù)有近乎百分百的保障,,但缺點(diǎn)是,倘若事務(wù)失敗,,此時(shí)不會(huì)留下太多信息,。換言之,如果要獲取這些信息,,需要做一些額外的步驟,,比如在一些高級(jí)實(shí)現(xiàn)。

共享事務(wù)資源模式

這個(gè)模式不錯(cuò),,系統(tǒng)所有的事務(wù)資源由一個(gè)相同的資源提供支持進(jìn)而移除XA,,降低系統(tǒng)的復(fù)雜度,提高吞吐量,。當(dāng)然不能拿來處理所有的用例,,但卻是如XA般堅(jiān)固,而且處理速度更加的快,。共享事務(wù)資源模式作為一種保障存在與特定的平臺(tái)和處理場(chǎng)景中,。

一個(gè)簡(jiǎn)單熟悉的例子就是共享一個(gè)數(shù)據(jù)庫(kù)的Connection,它存在于一個(gè)對(duì)象關(guān)系模型(ORM)控件和一個(gè)JDBC控件之間,。Spring事務(wù)管理器就是如此,,它支持ORM工具,比如Hibernate,、EclipseLink以及Java Persistence API(JPA),。相同的事務(wù)能安全的跨越ORM和JDBC控件之間,通常此事務(wù)是由service層受事務(wù)控制的執(zhí)行方法所驅(qū)動(dòng)的,。

此模式的另外一個(gè)特點(diǎn)是,,消息驅(qū)動(dòng)的單個(gè)數(shù)據(jù)庫(kù)更新,如本文初始階段的簡(jiǎn)單例子,。消 息中間件系統(tǒng)需要存儲(chǔ)這些數(shù)據(jù),,通常是關(guān)系型數(shù)據(jù)庫(kù)。實(shí)現(xiàn)這種模式,,需要將消息系統(tǒng)指定到相同的用于存儲(chǔ)業(yè)務(wù)數(shù)據(jù)的數(shù)據(jù)庫(kù)中,。這種模式依賴消息中間件供應(yīng) 商所提供的存儲(chǔ)策略細(xì)節(jié),以便能夠?qū)⑾⒅虚g件配置在相同的數(shù)據(jù)庫(kù)中,,并嵌入相同的事務(wù)處理,。

不是所有的供應(yīng)商都提供了此種模式,不過一種可替代,,幾乎可以用于任何數(shù)據(jù)庫(kù)的方式,,即利用Apache ActiveMQ的傳遞消息,并且插入一個(gè)存儲(chǔ)策略進(jìn)入消息代理中。一旦你知道了其中的訣竅,,配置起來很容易的,,我會(huì)在本文的shared-jms-db 項(xiàng)目案例中演示。此模式的所用的代碼無需關(guān)注,,它們會(huì)在Spring配置中得到聲明,。

名為 SynchronousMessageTriggerAndRollbackTests 的案例中,,單元測(cè)試校驗(yàn)所有與同步消息接收者相關(guān)的訊息,。 testReceiveMessageUpdateDatabase方法接受兩個(gè)消息,接著利用消息插入兩條記錄到數(shù)據(jù)庫(kù)中,。如果此方法退出,,測(cè)試框架回 滾事務(wù),這樣就能校驗(yàn)消息和數(shù)據(jù)庫(kù)更新是否發(fā)生回滾,,如清單3所示:

清單3,、驗(yàn)證消息和數(shù)據(jù)庫(kù)更新是否回滾

1
2
3
4
5
6
7
8
@AfterTransaction
public void checkPostConditions() {
  assertEquals(0, SimpleJdbcTestUtils.countRowsInTable(jdbcTemplate, "T_FOOS"));
  List<String> list = getMessages();
  assertEquals(2, list.size());
}

配置文件最重要的部分就是ActiveMQ的持久化策略,連接消息系統(tǒng)和相同數(shù)據(jù)源作為業(yè)務(wù)數(shù)據(jù),,Spring JmsTemplate的flag標(biāo)簽用來接收消息,。清單4 展示了如何配置ActiveMQ持久化策略:

清單4、ActiveMQ持久化配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<bean id="connectionFactory" depends-on="brokerService">
      <property name="brokerURL" value="vm://localhost?async=false" />
</bean>
<bean id="brokerService" init-method="start" destroy-method="stop">
       <property name="persistenceAdapter">
          <bean>
            <property name="dataSource">
                 <bean>
                      <property name="targetDataSource" ref="dataSource"/>
                      <property name="jmsTemplate" ref="jmsTemplate"/>
                 </bean>
            </property>
            <property name="createTablesOnStartup" value="true" />
          </bean>
       </property>
</bean>

清單5展示了Spring JmsTemplate中用來接收消息的flag標(biāo)簽:

清單5,、設(shè)置JmsTemplate事務(wù)應(yīng)用

1
2
3
4
<bean id="jmsTemplate">
        <!-- This is important... -->
        <property name="sessionTransacted" value="true" />
</bean>

若 sessionTransacted不為true,,JMS session transaction API就無法被調(diào)用,消息接受者將無法回滾,。最為重要的是,,嵌入的代理包含一個(gè)特殊的async=false參數(shù)以及DataSource外包類,這樣就可以確保ActiveMQ擁有同Spring一樣的事務(wù)JDBC Connection,。

一個(gè)共享數(shù)據(jù)庫(kù)源可以由獨(dú)立的單個(gè)數(shù)據(jù)源組成,,特別是這些數(shù)據(jù)源擁有同樣的RDBMS平臺(tái)。企業(yè)級(jí)數(shù) 據(jù)庫(kù)供應(yīng)商均提供同名概念(the notion of synonyms)支持,,表可以作為一個(gè)同名(synonyms)聲明于多個(gè)schema中,。借助這個(gè)手段,分布在不同物理平臺(tái)上的數(shù)據(jù),,可以均可 JDBC client同意Connection事務(wù)管理,。比如在一個(gè)真實(shí)的系統(tǒng)中,采用ActiveMQ共享資源模式實(shí)現(xiàn),,通常需要為消息和業(yè)務(wù)數(shù)據(jù)創(chuàng)建同名,。

性能和JDBCPersistenceAdapter

ActiveMQ 社區(qū)的一些開發(fā)人員表示JDBCPersistenceAdapter會(huì)引發(fā)性能問題。然而,,許多項(xiàng)目和上線系統(tǒng)采用ActiveMQ與關(guān)系型數(shù)據(jù)庫(kù)搭配使 用,。在這些案例中,公認(rèn)的是日志版本的適配器可以用來提供性能,當(dāng)然這對(duì)共享資源模式來說是不利的,,因?yàn)槿罩颈旧砭褪且粋€(gè)新的事務(wù)資源,。然而,對(duì)于 JDBCPersistenceAdapter大家眾說紛紜,,看法不一,。確實(shí),有理由相信采用共享資源或許能提高日志案例的性能,。這個(gè)一結(jié)論來自 Sping以及AtiveMQ工程師團(tuán)隊(duì)的研究,。

非消息場(chǎng)景(多個(gè)數(shù)據(jù)庫(kù))的另一種共享資源技術(shù)就是,在RDBMS平臺(tái)一級(jí)利用Oracle數(shù)據(jù)庫(kù)鏈接一個(gè)特征到兩個(gè)數(shù)據(jù)庫(kù)schema中,?;蛟S這需要改變應(yīng)用代碼,或者創(chuàng)建同名,,因?yàn)楸淼膭e名會(huì)指向一個(gè)已鏈接數(shù)據(jù)庫(kù),,此數(shù)據(jù)庫(kù)包含鏈接的名稱。

最大努力一次提交模式

開發(fā)人員必須知道,,最大努力一次提交模式應(yīng)用相當(dāng)?shù)钠毡?,但是在一些?chǎng)景并不適用。這是一種非XA模式,,它包含一 個(gè)同步大量資源單一相提交(single-phase commit),。參與者應(yīng)該意識(shí)到這種折中,如果不用兩階段提交,,那最大努力一次提交模式的安全性不如XA事務(wù)但也是相當(dāng)不錯(cuò),。許多海量數(shù)據(jù)、大吞吐量事 務(wù)處理系統(tǒng)用最大努力一次提交模式提高性能,。

最基本的理念就是在單一事務(wù)中盡可能的延遲所有資源的提交,,這樣唯一可能發(fā)生錯(cuò)誤的就是基礎(chǔ)組件,而非業(yè)務(wù)處理錯(cuò)誤,。 采用最大努力一次提交模式的系統(tǒng)假定基礎(chǔ)組件出錯(cuò)的可能性非常小,,因此能夠承受風(fēng)險(xiǎn)獲得較高的吞吐量收益。如果業(yè)務(wù)處理服務(wù)也被設(shè)計(jì)為一個(gè)幕等式 (idempotent),,發(fā)生錯(cuò)誤的可能性也很小,。(譯者注:幕等式是數(shù)學(xué)和計(jì)算機(jī)科學(xué)特定運(yùn)算的一個(gè)特性,應(yīng)用初始化以后多次操作其結(jié)果都不會(huì)再發(fā)生改變)

為了幫助大家更好的理解這個(gè)模式分析失敗結(jié)果,,我會(huì)用消息驅(qū)動(dòng)數(shù)據(jù)庫(kù)更新作為例子來加以說明,。

本事務(wù)中兩個(gè)資源將被統(tǒng)計(jì)、并且計(jì)算,。消息事務(wù)在一個(gè)數(shù)據(jù)庫(kù)之前開啟,,然后逆序結(jié)束,。成功的順序或許和本文開始的時(shí)候一模一樣:

  1. 開啟消息事務(wù)
  2. 接受消息
  3. 開啟數(shù)據(jù)庫(kù)事務(wù)
  4. 更新數(shù)據(jù)庫(kù)
  5. 提交數(shù)據(jù)庫(kù)事務(wù)
  6. 提交消息事務(wù)

準(zhǔn)確的說,此順序前四個(gè)步驟都不重要,,重要的是消息必須在數(shù)據(jù)庫(kù)更新之前被接受,,并且每個(gè)事務(wù)必須在對(duì)應(yīng)的資源被調(diào)用之前開啟,因此合理的順序應(yīng)該如下:

  1. 開啟消息事務(wù)
  2. 開啟數(shù)據(jù)庫(kù)事務(wù)
  3. 接受消息
  4. 更新數(shù)據(jù)庫(kù)
  5. 提交數(shù)據(jù)庫(kù)事務(wù)
  6. 提交消息事務(wù)

關(guān)鍵點(diǎn)是最后兩步很重要,,它們必須放在最后按順序執(zhí)行,。按序之所以重要,其本身就是一個(gè)技術(shù)問題,,不過這個(gè)順序是有業(yè)務(wù)需 要決定的,。這個(gè)順序告訴開發(fā)者,其中的一個(gè)事務(wù)資源是特殊的,,這個(gè)資源包含了如何在其他資源上運(yùn)作的指令,。這是一個(gè)業(yè)務(wù)順序:系統(tǒng)不能自動(dòng)告知走事務(wù)到哪一步了,。即使消息和數(shù)據(jù)庫(kù)是兩個(gè)資源,,事務(wù)也常常遵循這一流程。按序之所以重要,,是因?yàn)槭聞?wù)必須處理失敗案例,。迄今最常見的失敗案例是,諸如壞數(shù)據(jù),、編程錯(cuò)誤等失敗的業(yè)務(wù)處理,。本例中,這兩個(gè)事務(wù)很容易用來相應(yīng)一個(gè)異常并且回滾,。這樣,,業(yè)務(wù)數(shù)據(jù)的完成性得到保障,時(shí)間線與本文開始列出的理想失敗案例神似,。

引發(fā)回滾機(jī)制的精確性不是很重要的,,這樣的機(jī)制有好一些。最重要的是,,提交或者回滾必須按照資源中業(yè)務(wù)順序的逆序發(fā)生,。在一個(gè)應(yīng)用案例中,消息事務(wù)必須在最后提交,,因?yàn)樘幚順I(yè)務(wù)的指令包含在這個(gè)資源中,,這是因?yàn)椋苌儆惺“咐?其第一次提交成功,,第二次失敗,。在這個(gè)點(diǎn)上,所有設(shè)計(jì)業(yè)務(wù)處理的部分都已完成,,那唯一能引起部分失敗的因素,,可能消息中間件的基礎(chǔ)問題。

注意如果 數(shù)據(jù)庫(kù)資源提交失敗,那么事務(wù)最終會(huì)發(fā)生回滾,。所以是說非原子性失敗模型(failure mode)其第一個(gè)事務(wù)會(huì)提交,,第二個(gè)事務(wù)發(fā)生回滾。通常,,事務(wù)中有n個(gè)資源,,那么就存在n-1個(gè)這樣的失敗模型,這會(huì)導(dǎo)致一個(gè)子事務(wù)回滾后,,其它一些資源處在提交后的不一致狀態(tài),。在消息數(shù)據(jù)庫(kù)用例中,失敗模型的結(jié)局是,,消息會(huì)回滾,,然后是其他的事務(wù),即使其他這些事務(wù)都經(jīng)成功處理,;可以斷定最糟糕的事情 就是重復(fù)消息(duplicate message)被傳遞過來,。什么是重復(fù)消息呢?通常情況下,,事務(wù)中的早期資源被認(rèn)為是包含有后續(xù)資源處理流程的訊息,,因此失敗模型的結(jié)果可以被認(rèn)為就是重復(fù)消息。

一 些富有冒險(xiǎn)精神的人認(rèn)為重復(fù)消息發(fā)生的可能性微乎其微,,因此懶得去預(yù)測(cè)這些消息,。然而,為了更加確信業(yè)務(wù)數(shù)據(jù)是準(zhǔn)確性和一致性,,還是需要在業(yè)務(wù)邏輯層面對(duì) 此有清晰地認(rèn)識(shí),。 如果懷疑重復(fù)消息可能發(fā)生,那么必須核實(shí),,業(yè)務(wù)處理過程是否處理過數(shù)據(jù),,在處理數(shù)據(jù)之前是否什么都沒做。這個(gè)特定的說明有時(shí)指幕等業(yè)務(wù)服務(wù)模型 (Idempotent Business Service pattern),。

相關(guān)案例包括兩個(gè)采用此模型的同步事務(wù)資源例子,,我會(huì)在后面做一一分析,以及一些其它選項(xiàng),。

Spring和消息驅(qū)動(dòng)POJO

案例best-jms-db項(xiàng)目中的代碼,,開發(fā)人員采用主流配置,這樣就可以使用最大努力一次提交模式,。具體的做法是這樣的,,通過一個(gè)異步的監(jiān)聽器將消息傳給一個(gè)隊(duì)列,并將此數(shù)據(jù)插入 數(shù)據(jù)庫(kù)表中,。

TransactionAwareConnectionFactoryProxy 是Spring的一個(gè)存儲(chǔ)控件,,應(yīng)用于這個(gè)模式中,,也是最關(guān)鍵的組成部分。放棄采用供應(yīng)商提供的粗顆粒度的 ConnectionFactory,,configuration采用裝飾模式包裝了一個(gè)ConnectionFactory,,用它來處理事務(wù)同步問題。 具體配置見jms-context.xml,如下清單6所示:

清單6,、配置一個(gè)TransactionAwareConnectionFactoryProxy去包裝一個(gè)供應(yīng)商提供的JMS ConnectionFactory

1
2
3
4
5
6
7
8
<bean id="connectionFactory">
        <property name="targetConnectionFactory">
            <bean depends-on="brokerService">
                <property name="brokerURL" value="vm://localhost"/>
            </bean>
        </property>
        <property name="synchedLocalTransactionAllowed" value="true" />
</bean>

ConnectionFactory 無需知道哪個(gè)事務(wù)管理器與其同步,,每一時(shí)刻僅有一個(gè)事務(wù)處在活動(dòng)(active)狀態(tài)。這些是由Spring內(nèi)部在處理,。事務(wù)驅(qū)動(dòng)是由配置在data- source-context.xml中的DataSourceTransactionManager完成的,,事務(wù)管理器必須由輪詢和接受消息的JMS監(jiān)聽器容器監(jiān)控。

1
2
3
<jms:listener-container transaction-manager="transactionManager" >
    <jms:listener destination="async" ref="fooHandler" method="handle"/>
</jms:listener-container>

fooHandler和方法會(huì)告知監(jiān)聽器容器某個(gè)具體的控件的具體方法得到調(diào)用,,當(dāng)一個(gè)消息達(dá)到”異步”隊(duì)列,。handler是如此實(shí)現(xiàn)的,接受一個(gè)String作為參數(shù)消息,,并將其作為數(shù)據(jù)插入記錄中,。

1
2
3
4
5
6
public void handle(String msg) {
  jdbcTemplate.update(
      "INSERT INTO T_FOOS (ID, name, foo_date) values (?, ?,?)", count.getAndIncrement(), msg, new Date());
}

為了模擬失敗,代碼用一個(gè)FailureSimulator切面,,它會(huì)檢查消息內(nèi)容是否真的失??;如清單7所示,,在FooHandler在事務(wù)結(jié)束之前,處理消息之后,,maybeFail()方法得到調(diào)用,,所以它能影響事務(wù)的結(jié)果。

清單7,、maybeFail()方法

1
2
3
4
5
6
7
8
9
10
@AfterReturning("execution( ..*Handler+.handle(String)) && args(msg)")
public void maybeFail(String msg) {
         if (msg.contains("fail")) {
               if (msg.contains("partial")) {
                      simulateMessageSystemFailure();
               } else {
                 simulateBusinessProcessingFailure();
               }
         }
}

simulateBusinessProcessingFailure() 方法會(huì)拋出DataAccessException,,就像數(shù)據(jù)庫(kù)訪問真的失敗。一旦這個(gè)方法被調(diào)用,,最理想的結(jié)局是所有的數(shù)據(jù)庫(kù)以及消息事務(wù)都能回滾,。 這個(gè)場(chǎng)景在案例項(xiàng)目AsynchronousMessageTriggerAndRollbackTests單元測(cè)試得到測(cè)試。

simulateMessageSystemFailure() 方法通過破壞底層的JMS Session來模擬消息系統(tǒng)失敗,。預(yù)期的結(jié)果是事務(wù)部分提交:數(shù)據(jù)庫(kù)提交了,,但消息回滾。這個(gè)在 synchronousMessageTriggerAndPartialRollbackTests單元測(cè)試驗(yàn)證過,。

同樣,,在AsynchronousMessageTriggerSunnyDayTests類中,包含一個(gè)所有事務(wù)成功提交的單元測(cè)試,。

相 同的JMS配置,,相同的業(yè)務(wù)邏輯同樣可以用在同步的環(huán)境中,,消息由存儲(chǔ)在業(yè)務(wù)邏輯中的阻塞請(qǐng)求所接收,而非監(jiān)聽器容器,。此方法在best-jms-db案 例項(xiàng)目中得到展示,。 sunny-day case以及事務(wù)全部回滾分別在SynchronousMessageTriggerSunnyDayTests和 SynchronousMessageTriggerAndRollbackTests得到測(cè)試。

鏈?zhǔn)绞聞?wù)管理器

在其他的最大努力一階段提交模式案例中,,一個(gè)粗糙的事務(wù)管理器實(shí)現(xiàn)僅僅是將一系列其他的事務(wù)管理器鏈接在一起,,去實(shí)現(xiàn)事務(wù)同步。倘若業(yè)務(wù)處理成功,,所有的事務(wù)將會(huì)提交,, 否則它們都能回滾。

ChainedTransactionManager 接受一系列其他的事務(wù)管理器作為注入屬性,,如清單8所示:

清單8,、配置

1
2
3
4
5
6
7
8
9
10
11
12
<bean id="transactionManager">
       <property name="transactionManagers">
           <list>
               <bean>
                  <property name="dataSource" ref="dataSource" />
               </bean>
               <bean>
                   <property name="dataSource" ref="otherDataSource" />
               </bean>
           </list>
       </property>
</bean>

此配置簡(jiǎn)單的測(cè)試,僅是同時(shí)插入數(shù)據(jù)到兩個(gè)數(shù)據(jù)庫(kù),,回滾,,同時(shí)確保兩個(gè)運(yùn)行回滾到最初狀態(tài)。此實(shí)現(xiàn)作為存在MulipleDataSourceTests中的一個(gè)單元測(cè)試,,如同XA案例中的 atomikos-db項(xiàng)目,。倘若回滾沒有同步,有事務(wù)提交了,,那測(cè)試就算失敗,。

記 住,資源順序很重要,,它們是嵌套的,,提交或者回滾以它們參與的相反順序進(jìn)行。其中一個(gè)事務(wù)最為特別:如果存在問題,,最重要的事務(wù)會(huì)回滾,,即便是這問題是一個(gè)資源失敗。 同樣,,testInsertWithCheckForDuplicates()測(cè)試方法展示了幕等式業(yè)務(wù)處理如何從部分失敗中保護(hù)系統(tǒng),,此實(shí)現(xiàn)作為一個(gè)里層資源(otherDataSource)中業(yè)務(wù)運(yùn)算防御檢測(cè)。

1
2
int count = otherJdbcTemplate.update("UPDATE T_AUDITS ... WHERE id=, ...?");
if (count == 0) { count = otherJdbcTemplate.update("INSERT into T_AUDITS ...", ...); }

update首先嘗試和一個(gè)where子句執(zhí)行,,不出意外,,update中的數(shù)據(jù)會(huì)插入數(shù)據(jù)庫(kù)中。本例中的幕等式處理一個(gè)額外的花銷是sunny-day case中額外的查詢,,這個(gè)額外的花銷在復(fù)雜的每個(gè)事務(wù)執(zhí)行多個(gè)查詢的業(yè)務(wù)處理中微乎其微,。

其他選擇

案例中的ChainedTransactionManager擁有簡(jiǎn)潔優(yōu)勢(shì),而且擴(kuò)展優(yōu)化已做的很好,。另一個(gè)方式是,, 當(dāng)?shù)诙€(gè)資源加入時(shí),,利用Spring的TransactionSychronization API給當(dāng)前事務(wù) 注冊(cè)一個(gè)回調(diào)函數(shù),此方式在best-jms-db案例中,,最大的特點(diǎn)是TransactionAwareConnectionFactory和一個(gè) DataSourceTransactionManager的結(jié)合,。利用TransactionSynchronizationManager,這個(gè)特殊的案例可以擴(kuò)展并且泛化到包含non-JMS的資源中,。這樣理論上有個(gè)優(yōu)勢(shì),,就是只有加入事務(wù)的資源得到支持,而非鏈上的 所有資源,。然而配置依舊需要監(jiān)聽某個(gè)潛在的事務(wù)與之對(duì)應(yīng)的資源,。

同樣,Spring工程師團(tuán)隊(duì)考慮將最大努力一階段提交事務(wù)管理器特性作為Spring核心,。你可以在JJRA issue中投票,,如果你喜歡這種模式,希望Spring中顯示以及更加透明地支持此種模式,。

非事務(wù)訪問模式

非事務(wù)訪問模式需要一個(gè)特殊的業(yè)務(wù)處理,,這樣才有意義。理想的狀態(tài)是有時(shí),,其中一些你需要訪問的資源邊緣化,,一點(diǎn)都不需要事務(wù)。比如,,或許你需要將一行數(shù)據(jù)插入一個(gè) 審核表中,,此操作是獨(dú)立的,和業(yè)務(wù)事務(wù)是否成功無關(guān),。僅僅記錄試圖做了某事,。更普遍的場(chǎng)景,人們高估了他們需要對(duì)其中一個(gè)資源做讀寫的頻次,,事實(shí)上,只有 訪問就很好了,。 否則,,寫操作需要得到很好地控制,因此如果發(fā)生任何錯(cuò)誤,,寫操作可以被記錄下來或者忽略,。

在以上的案例中資源恰當(dāng)?shù)卦砣质聞?wù),但仍然有其自己的本地事務(wù),,本地事務(wù)無需與其他發(fā)生的事情保持同步,。如果你使用的是Spring,主要的事務(wù)由一個(gè)PlatformTransactionManager驅(qū)動(dòng),, 邊緣資源或許是一個(gè)數(shù)據(jù)庫(kù)Connection,,它來自一個(gè)不受事務(wù)管理器控制的DataSource,。每一次訪問邊緣資源需要將缺省環(huán)境設(shè)為 autoCommit=true。updates對(duì)讀操作不可見,,前者可以與其他非提交事務(wù)并發(fā)進(jìn)行,,但寫操作帶來的影響通常對(duì)其他操作來說是立即可見的。

這個(gè)模式需要更多精細(xì)地分析,,以及更多自信去涉及業(yè)務(wù)處理,,但它同最大努力一階段提交沒 什么區(qū)別。當(dāng)任何事情出錯(cuò),,一個(gè)通用的補(bǔ)償事務(wù)服務(wù)對(duì)多數(shù)項(xiàng)目來說太過龐大,。不過簡(jiǎn)單的用例所涉及的服務(wù),它是幕等式的僅僅執(zhí)行一個(gè)寫的操作,,這種現(xiàn)象再 普通不過了,。這些是非事務(wù)策略的理想場(chǎng)景。

Wing-and-a-Prayer:一種反模式

最后一種模式是一種反模式,,它出現(xiàn)這樣一個(gè)場(chǎng)景中,,開發(fā)者不理解或者沒有意識(shí)到他們已經(jīng)存在一個(gè)分布式事務(wù)時(shí)。無需顯示的調(diào)用底層的資源事務(wù)API,,你不確 定所有的資源是否在一個(gè)事務(wù)中,。倘若用的是一個(gè)Spring事務(wù)管理器而非JtaTransactionManager,此管理器會(huì)將一個(gè)事務(wù)資源加入其中,。這個(gè)事務(wù)管理器將會(huì)攔截Spring聲明事務(wù)管理特性的執(zhí)行方法,,比如@Transactional;其他的資源不會(huì)注冊(cè)到相同的事務(wù)中,。通常的結(jié)局 是任何事情都運(yùn)轉(zhuǎn)正常,,不過很快用戶會(huì)發(fā)現(xiàn)存在一個(gè)異常,其中一個(gè)資源沒有回滾,。一個(gè)典型的錯(cuò)誤導(dǎo)致的問題是利用一個(gè) DataSourceTransactionManager以及一個(gè)利用Hibernate實(shí)現(xiàn)的倉(cāng)庫(kù),。

該用哪個(gè)模式呢?

我會(huì)通過分析已介紹過的模式其利弊,,幫助大家做出取舍,。第一步是分析你的系統(tǒng)是否需要分布式事務(wù)。一個(gè)必須但不充分條件是,,存在不止一個(gè)事務(wù)資源的單一處理,。充分條件是這些資源都在一個(gè)單獨(dú)的用例中,通常由系統(tǒng)架構(gòu)的service層調(diào)用來驅(qū)動(dòng),。

如果你不認(rèn)為這是分布式事務(wù),,那最好采用Wing-and-a-Prayer模式,接著你會(huì)看見數(shù)據(jù)應(yīng)該回滾但沒有,?;蛟S你會(huì)看到這種影響從失敗發(fā)生直至其下游一直存在,,而且很難追溯回去。Wing-and-a-Prayer的 使用也可能會(huì)開發(fā)人員所疏忽,,他們認(rèn)為受到了XA保障,,其實(shí)并沒與配置底層資源加入到事務(wù)中。我曾經(jīng)做過一個(gè)這樣的項(xiàng)目,,數(shù)據(jù)庫(kù)是其他人員搭建的,,他們?cè)?安裝數(shù)據(jù)庫(kù)的過程中關(guān)閉了XA支持。運(yùn)行了個(gè)把月沒有任何問題,,接著各種奇怪的失敗開始侵入業(yè)務(wù)處理中,,需要花很長(zhǎng)的時(shí)間去找出問題。

如果是一個(gè)包含異質(zhì)資源的簡(jiǎn)單用例,,你可以分析甚至做一些重構(gòu),,那么非事務(wù)訪問模式或 許是個(gè)不錯(cuò)的選擇,特別是其中一個(gè)幾乎是只讀資源,,雙檢測(cè)確保寫操作,。即便是失敗了,非事務(wù)資源中的數(shù)據(jù)在業(yè)務(wù)術(shù)語(yǔ)中必須有意義,。審核,、版本控制、甚至日 志信息能很好的切入到此目錄中,,失敗變得相對(duì)很平?!?任何時(shí)間真實(shí)事務(wù)中的任何事情都可回滾,但你需確信這樣做不存在負(fù)面影響就好,。

對(duì)系統(tǒng)而言,,最大努力一階段提交需要通用的失敗保護(hù)機(jī)制,但有不存在2PC那么大的開銷,,而且性能得到極大的提升,。相對(duì)非事務(wù)資源,它的建立需要更多的技巧,,但無需太多的分析,, 通常應(yīng)用于更加通用的數(shù)據(jù)類型中。完成數(shù)據(jù)一致性的某些特性,,需要保障業(yè)務(wù)處理對(duì)外層資源(”outer” resources:第一個(gè)提交的資源)而言是幕等式。消息驅(qū)動(dòng)的數(shù)據(jù)庫(kù)更新就是一個(gè)完美的例子,,并且在Spring中得到很好的支持,。不常見的場(chǎng)景需要一些額外的框架代碼,這些代碼終究會(huì)成為Spring的一部分,。

共享資源模式是一種特定的例子,,通常涉及一個(gè)特定的類型和平臺(tái)兩個(gè)資源,,比如,ActiveMQ和任何一個(gè)RDBMS或者OracleAQ與一個(gè)Oracle數(shù)據(jù)庫(kù)共存,。這樣做最大的收益是相當(dāng)?shù)撵`活以及出色的性能,。

Full XA with 2PC是一種通用模式,在應(yīng)對(duì)多個(gè)異質(zhì)資源事務(wù) 失敗時(shí)提供很好的無憂保證,。不利的是它的開銷很大,,需要遵循特定的I/O協(xié)議和特定的平臺(tái)。有開源的JTA實(shí)現(xiàn),,提供了一種擺脫應(yīng)用服務(wù)器的方式,,但多數(shù) 開發(fā)人員依舊認(rèn)為它們并非最好??梢源_信的是,,人們花更多的時(shí)間去思考系統(tǒng)的事務(wù)界限,會(huì)更傾向于使用他們并不那么需要的JTA和XA,。至少使用 Spring的開發(fā)人員,,他們的業(yè)務(wù)邏輯無需知道事務(wù)如何被處理的,暫時(shí)無需考慮平臺(tái)選擇的問題,。

原文鏈接: javaworld 翻譯: ImportNew.com - 喬永琪
譯文鏈接: http://www./15812.html
[ 轉(zhuǎn)載請(qǐng)保留原文出處,、譯者和譯文鏈接。]

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,,所有內(nèi)容均由用戶發(fā)布,,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式,、誘導(dǎo)購(gòu)買等信息,,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請(qǐng)點(diǎn)擊一鍵舉報(bào),。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多