JAVA分散式交易原理及應用

來源:互聯網
上載者:User

JTA(Java Transaction API)允許應用程式執行分散式交易處理--在兩個或多個網路電腦資源上訪問並且更新資料。JDBC驅動程式的JTA支援極大地增強了資料訪問能力。 

  本文的目的是要提供一個關於的Java交易處理API(JTA)的進階的概述,以及與分散式交易相關的內容。一個交易處理定義了一個工作邏輯單元,要麼徹底成功要麼不產生任何結果。 一個分散式交易處理只是一個在兩個或更多網路資源上訪問和更新資料的交易處理,因此它在那些資源之間必然是等價的。在本文中,我們主要關心的是如何處理關聯式資料庫系統。 

  我們要討論的分散式交易處理(DTP)模型中包含的組件是: 
    應用程式 
    應用程式伺服器 
    交易管理程式 
    資來源配接器 
    資源管理程式 

  在以後的內容中,我們將描述這些組件以及它們與JTA和資料庫訪問的關係。 

  訪問資料庫 

  最好把分散式交易處理中包含的組件看作是獨立的過程,而不是考慮它們在一個特定的電腦中的位置。這些組件中的一些可以儲存在單機中,或者也可在好幾台機器之間分布。 下面例子中的圖表可以顯示在一台特定的電腦上的組件,但是這些操作之間的關係是必須首要考慮的。 

  最簡單的例子:用於本機資料庫交易處理的應用程式 

  關聯式資料庫訪問的最簡單的形式僅僅包括應用程式、資源管理程式和資來源配接器。應用程式只不過是發送請求到資料庫並且從資料庫中擷取資料的終端使用者訪問點 
  我們討論的資源管理程式是一個關聯式資料庫管理系統(RDBMS),比如Oracle或者SQL Server。所有的實際資料庫管理都是由這個組件處理的。 
  資來源配接器是外部空間之間的通訊管道組件,或者是請求翻譯器,在本例中,是應用程式和資源管理程式。在我們的討論中,這是一個JDBC驅動程式。 
  下面的描述是資源管理程式本地交易處理的一個描述,也就是說,一個交易處理被被限制在一個特定的企業資料庫。 
  應用程式發送一個用於JDBC驅動程式資料的請求,然後翻譯這個請求並把它通過網路發送到資料庫中。 資料庫把資料發送回驅動程式,然後把翻譯的結果發送回應用程式,如所示: 

 
這個例子說明了在一個簡化的系統中的基本的資訊流;然而,今天的企業使用的應用程式伺服器都添加了其他的組件到這個過程處理中。 

  應用程式伺服器 
  應用程式伺服器是交易處理操作的另一個組件。應用程式伺服器處理大部分的應用程式操作並且獲得終端使用者應用程式的一些負載。基於前面的例子,我們可以看出應用程式伺服器在交易處理上添加了另一個操作層: 

 
到目前為止,我們的例子說明了單個的本地交易處理,並且描述了分散式交易處理模型的五個組件中的四個。第五個組件,交易管理程式只有當事務將要被分配的時候才會開始被考慮。 

  分散式交易處理和交易管理程式 

  像我們前面所提到的,一個分散式交易處理是一個在兩個或更多網路資源上訪問和更新資料的交易處理。 
  這些資源可以由好幾個位於一個單獨伺服器上的不同的關係型資料庫管理系統組成,比如說Oracle、SQL Server和Sybase;它們也可以包含存在於若干不同的伺服器上的同一種資料庫的若干個執行個體。在任何情況下,一個分散式交易處理包括各種的資源管理程式之間的協同作用。這個協同作用是交易管理函數。 
  交易管理程式負責作出要麼提交(commit)要麼退回(rollback)任何分散式交易處理的決定。一個提交決定應該導致一個成功的交易處理;而退回操作則是保持資料庫中的資料不變。 JTA指定一個分散式交易處理中的交易管理程式和另一個組件之間的標準Java介面:應用程式,應用程式伺服器和資源管理程式。 這個關係被顯示在下面的圖表中: 
 
  在交易管理程式周圍的數字框框相應於JTA的三個介面部分: 

  1—UserTransaction—javax.transaction.UserTransaction介面提供能夠編程地控制交易處理範圍的應用程式。 javax.transaction.UserTransaction方法開啟一個全域事務並且使用調用線程與交易處理關聯。 
  2—Transaction Manager—javax.transaction.TransactionManager介面允許應用程式伺服器來控制代表正在管理的應用程式的事務範圍。 
  3—XAResource—javax.transaction.xa.XAResource介面是一個基於X/Open CAE Specification的行業標準XA介面的Java映射。 

  注意,一個限制性環節是通過JDBC驅動程式的XAResource介面的支援。JDBC驅動程式必須支援兩個正常的JDBC互動作用:應用程式和/或應用程式伺服器,而且以及JTA的XAResource部分。 

  編寫應用程式水平代碼的開發人員不會關心分散式交易處理管理的細節。 這是分散式交易處理基本結構的工作—應用程式伺服器、交易管理程式和JDBC驅動程式。應用程式代碼中唯一的需要注意的就是當串連處於一個分散式交易範圍內的時候,不應該調用一個會影響事務邊界的方法。特別的是,一個應用程式不應該調用Connection方法commit、rollback和setAutoCommit(true),因為它們將破壞分散式交易的基本結構管理。 

  分散式交易處理 
  交易管理程式是分散式交易基本結構的基本組件;然而JDBC驅動程式和應用程式伺服器組件應該具備下面的特徵: 
  驅動程式應該實現JDBC 2.0應用程式介面,包括Optional Package介面XADataSource和XAConnection以及JTA介面XAResource。 
  應用程式伺服器應該提供一個DataSource類,用來實現與分散式交易基本結的互動以及一個串連池模組(用於改善效能)。 
  分散式交易處理的第一步就是應用程式要發送一個事務請求到交易管理程式。雖然最後的commit/rollback決定把事務作為一個簡單的邏輯單元來對待,但是仍然可能會包括許多事務分支。一個事務分支與一個到包含在分散式交易中的每個資源管理程式相關聯。因此,到三個不同的關聯式資料庫管理的請求需要三個事務分支。每個事務分支必須由本地資源管理程式提交或者返回。交易管理程式控制事務的邊界,並且負責最後決定應該提交或者返回的全部事務。 這個決定由兩個步驟組成,稱為Two - Phase Commit Protocol。 

  在第一步驟中,交易管理程式輪詢所有包含在分散式交易中的資源管理程式(關聯式資料庫管理)來看看哪個可以準備提交。如果一個資源管理程式不能提交,它將不響應,並且把事務的特定部分返回,以便資料不被修改。 

  在第二步驟中,交易管理程式判斷否定響應的資源管理程式中是否有能夠返回整個事務的。如果沒有否定響應的話,翻譯管理程式提交整個事務並且返回結果到應用程式中。 

  開發事項管理程式碼的開發人員必須與所有三個JTA介面有關:UserTransaction、TransactionManager和XAResource,這三個介面都被描述在 

  Sun JTA specification中。JDBC驅動程式開發人員只需要關心XAResource介面。這個介面是允許一個資源管理程式參與事務的行業標準X/Open XA協議的Java映射。串連XAResource介面的驅動程式組件負責在交易管理程式和資源管理程式之間擔任"翻譯"的任務。下面的章節提供了XAResource調用的例子。 

   JDBC驅動程式和XAResource


  為了簡化XAResource的說明,這些例子說明了一個應用程式在不包含應用程式伺服器和事項管理程式的情況下應該如何使用JTA。 基本上,這些例子中的應用程式也擔任應用程式伺服器和事項管理程式的任務。大部分的企業使用交易管理程式和應用程式伺服器,因為它們能夠比一個應用程式更能夠高效地管理分散式交易。 然而遵循這些例子,一個應用程式開發人員可以測試在JDBC驅動程式中的JTA支援的健壯性。而且有一些例子可能不是工作在某個特定的資料庫上,這是因為關聯在資料庫上的一些內在的問題。 

  在使用JTA之前,你必須首先實現一個Xid類用來標識事務(在普通情況下這將由交易管理程式來處理)。Xid包含三個元素:formatID、gtrid(全域事務標識符)和bqual(分支修飾詞標識符)。 

  formatID通常是零,這意味著你將使用OSI CCR(Open Systems Interconnection Commitment, Concurrency和Recovery 標準)來命名。如果你要是用另外一種格式,那麼formatID應該大於零。-1值意味著Xid為無效。 

  gtrid和bqual可以包含64個位元組二進位碼來分別標識全域事務和分支事務。唯一的要求是gtrid和bqual必須是全域唯一的。此外,這可以通過使用指定在OSI CCR中的命名規則規範來完成。 

  下面的例子說明Xid的實現: 
import javax.transaction.xa.*;<br />public class MyXid implements Xid<br />{<br /> protected int formatId;<br /> protected byte gtrid[];<br /> protected byte bqual[];<br /> public MyXid()<br /> {<br /> }<br /> public MyXid(int formatId, byte gtrid[], byte bqual[])<br /> {<br />  this.formatId = formatId;<br />  this.gtrid = gtrid;<br />  this.bqual = bqual;<br /> }<br /> public int getFormatId()<br /> {<br />  return formatId;<br /> }<br /> public byte[] getBranchQualifier()<br /> {<br />  return bqual;<br /> }<br /> public byte[] getGlobalTransactionId()<br /> {<br />  return gtrid;<br /> }<br />}<br />
  其次,你需要建立一個你要使用的資料庫的資料來源: 
public DataSource getDataSource()<br /> throws SQLException<br /> {<br />  SQLServerDataSource xaDS = new<br />  com.merant.datadirect.jdbcx.sqlserver.SQLServerDataSource();<br />  xaDS.setDataSourceName("SQLServer");<br />  xaDS.setServerName("server");<br />  xaDS.setPortNumber(1433);<br />  xaDS.setSelectMethod("cursor");<br />  return xaDS;<br />}<br />
  

例1—這個例子是用“兩步提交協議”來提交一個事務分支: 
XADataSource xaDS;<br />XAConnection xaCon;<br />XAResource xaRes;<br />Xid xid;<br />Connection con;<br />Statement stmt;<br />int ret;<br />xaDS = getDataSource();<br />xaCon = xaDS.getXAConnection("jdbc_user", "jdbc_password");<br />xaRes = xaCon.getXAResource();<br />con = xaCon.getConnection();<br />stmt = con.createStatement();<br />xid = new MyXid(100, new byte[]{0x01}, new byte[]{0x02});<br />try {<br />  xaRes.start(xid, XAResource.TMNOFLAGS);<br />  stmt.executeUpdate("insert into test_table values (100)");<br />  xaRes.end(xid, XAResource.TMSUCCESS);<br />  ret = xaRes.prepare(xid);<br />  if (ret == XAResource.XA_OK) {<br />    xaRes.commit(xid, false);<br />   }<br />}<br />catch (XAException e) {<br /> e.printStackTrace();<br />}<br />finally {<br /> stmt.close();<br /> con.close();<br /> xaCon.close();<br />}
  因為所有這些例子中的初始化代碼相同或者非常相似,僅僅是一些重要的地方的代碼由不同。 

例2—這個例子,與例1相似,說明了一個返回過程: 
xaRes.start(xid, XAResource.TMNOFLAGS);<br />stmt.executeUpdate("insert into test_table values (100)");<br />xaRes.end(xid, XAResource.TMSUCCESS);<br />ret = xaRes.prepare(xid);<br />if (ret == XAResource.XA_OK) {<br /> xaRes.rollback(xid);<br />}
  

例3—這個例子說明一個分散式交易分支如何中止,讓相同的串連做本地交易處理,以及它們稍後該如何繼續這個分支。 分散式交易的兩步提交作用不影響本地事務。 
xid = new MyXid(100, new byte[]{0x01}, new byte[]{0x02});<br />xaRes.start(xid, XAResource.TMNOFLAGS);<br />stmt.executeUpdate("insert into test_table values (100)");<br />xaRes.end(xid, XAResource.TMSUSPEND);<br />∥這個更新在事務範圍之外完成,所以它不受XA返回影響。<br />stmt.executeUpdate("insert into test_table2 values (111)");<br />xaRes.start(xid, XAResource.TMRESUME);<br />stmt.executeUpdate("insert into test_table values (200)");<br />xaRes.end(xid, XAResource.TMSUCCESS);<br />ret = xaRes.prepare(xid);<br />if (ret == XAResource.XA_OK) {<br />xaRes.rollback(xid);<br />}

  例4—這個例子說明一個XA資源如何分擔不同的事務。 建立了兩個事務分支,但是它們不屬於相同的分散式交易。 JTA允許XA資源在第一個分支上做一個兩步提交,雖然這個資源仍然與第二個分支相關聯。 
xid1 = new MyXid(100, new byte[]{0x01}, new byte[]{0x02});<br />xid2 = new MyXid(100, new byte[]{0x11}, new byte[]{0x22});<br />xaRes.start(xid1, XAResource.TMNOFLAGS);<br />stmt.executeUpdate("insert into test_table1 values (100)");<br />xaRes.end(xid1, XAResource.TMSUCCESS);<br />xaRes.start(xid2, XAResource.TMNOFLAGS);<br />ret = xaRes.prepare(xid1);<br />if (ret == XAResource.XA_OK) {<br /> xaRes.commit(xid2, false);<br />}<br />stmt.executeUpdate("insert into test_table2 values (200)");<br />xaRes.end(xid2, XAResource.TMSUCCESS);<br />ret = xaRes.prepare(xid2);<br />if (ret == XAResource.XA_OK) {<br /> xaRes.rollback(xid2);<br />}
  例5—這個例子說明不同的串連上的事務分支如何串連成為一個單獨的分支,如果它們串連到相同的資源管理程式。這個特點改善了分散式交易的效率,因為它減少了兩步提交處理的數目。兩個串連到資料庫伺服器上的XA將被建立。每個串連建立它自己的XA資源,正規的JDBC串連和語句。在第二個XA資源開始一個事務分支之前,它將察看是否使用和第一個XA資源使用的是同一個資源管理程式。如果這是執行個體,它將加入在第一個XA串連上建立的第一個分支,而不是建立一個新的分支。 稍後,這個事務分支使用XA資源來準備和提交。 
xaDS = getDataSource();<br />xaCon1 = xaDS.getXAConnection("jdbc_user", "jdbc_password");<br />xaRes1 = xaCon1.getXAResource();<br />con1 = xaCon1.getConnection();<br />stmt1 = con1.createStatement();<br />xid1 = new MyXid(100, new byte[]{0x01}, new byte[]{0x02});<br />xaRes1.start(xid1, XAResource.TMNOFLAGS);<br />stmt1.executeUpdate("insert into test_table1 values (100)");<br />xaRes1.end(xid, XAResource.TMSUCCESS);<br />xaCon2 = xaDS.getXAConnection("jdbc_user", "jdbc_password");<br />xaRes2 = xaCon1.getXAResource();<br />con2 = xaCon1.getConnection();<br />stmt2 = con1.createStatement();<br />if (xaRes2.isSameRM(xaRes1)) {<br /> xaRes2.start(xid1, XAResource.TMJOIN);<br /> stmt2.executeUpdate("insert into test_table2 values (100)");<br /> xaRes2.end(xid1, XAResource.TMSUCCESS);<br />}<br />else {<br /> xid2 = new MyXid(100, new byte[]{0x01}, new byte[]{0x03});<br /> xaRes2.start(xid2, XAResource.TMNOFLAGS);<br /> stmt2.executeUpdate("insert into test_table2 values (100)");<br /> xaRes2.end(xid2, XAResource.TMSUCCESS);<br /> ret = xaRes2.prepare(xid2);<br /> if (ret == XAResource.XA_OK) {<br />  xaRes2.commit(xid2, false);<br /> }<br />}<br />ret = xaRes1.prepare(xid1);<br />if (ret == XAResource.XA_OK) {<br /> xaRes1.commit(xid1, false);<br />}

  例6—這個例子說明在錯誤恢複的階段,如何恢複準備好的或者快要完成的事務分支。 它首先試圖返回每個分支;如果它失敗了,它嘗試著讓資源管理程式丟掉關於事務的訊息。 
MyXid[] xids;<br />xids = xaRes.recover(XAResource.TMSTARTRSCAN | XAResource.TMENDRSCAN);<br />for (int i=0; xids!=null && i<br /> try {<br />  xaRes.rollback(xids[i]);<br /> }<br /> catch (XAException ex) {<br />  try {<br />   xaRes.forget(xids[i]);<br />  }<br /> catch (XAException ex1) {<br />  System.out.println("rollback/forget failed: " + ex1.errorCode);<br /> }<br />}<br />}  

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.