歡迎您光臨本站 註冊首頁

理解 JCA 事務

←手機掃碼閱讀     火星人 @ 2014-03-10 , reply:0

JCA 事務概述

  實際的示例會有助於展示一些常見的 JCA 事務問題. 在該例中,要完成的任務是要為一家公司構建電子商務應用程序,這家公司的主要業務是向客戶銷售工業製品.該公司決定構建一個 Web 站點,向更多的群體零售其產品.該 Web 站點允許任何使用瀏覽器的客戶訪問公司的主頁;瀏覽可用產品的目錄;查看詳細的價格信息、可用性、可用項目的說明;向購物車中添加購買的產品;最后購買產品.為了舉例說明必需的事務性行為,我把重點放在了客戶決定購買商品的用例上.圖 1 顯示了該應用程序的設計.

  圖 1. 訪問電子商務應用程序中的 EIS

  

  公司現有的 IT 基礎設施是圍繞兩大企業信息系統構建的.EIS1 是主機系統,在 CICS 下面運行 Cobol 事務.運行在這個系統上的事務將實現業務邏輯,並且還要訪問訂單錄入和客戶關係管理(CRM)所需要的數據.EIS2 是一個 IMS 系統,它包含產品說明目錄、價格信息和庫存控制.要支持所要求的功能,J2EE 應用程序必須能夠訪問來自這兩個系統的數據,並把它們無縫集成在一起.而要進行採購,則需要把以下操作作為一個工作單元(即作為一個事務)來執行:

  取得當前產品價格(EIS2).

  把客戶訂單輸入訂單系統(EIS1).

  給客戶記帳(EIS1).

  更新產品可用性信息(EIS2).

  步驟 2、步驟 3 或步驟 4 中的任何一個步驟的失敗,都會造成取消前面所做的所有操作.

  在該示例 Web 應用程序中,客戶機(Web 層中的 JSP 頁面)保存了一個對 CustomerSession 狀態會話 EJB 組件的一個實例的引用,並調用該實例上的方法.可以使用 CustomerSession 來保存購物車的內容、從目錄得到的產品信息以及客戶信息.如圖 1 所示,在與最終客戶交互期間,會話 bean 搜集並保存了採購商品所必需的、特定於產品選擇和客戶的一些信息.在 CustomerSession 中定義的方法,將調用無狀態會話 EJB 組件的方法(這個 bean 起到一個外觀(facade)的作用),例如 OrderService,從而調用 EIS 上的事務.

  在這個設計中,無狀態會話 bean OrderService 充當著 EIS1 的外觀.它定義了以下方法:

  OrderInfo addOrder(String customerId, ItemInfo itemInfo) 用 JCA 通用客戶機介面(CCI)調用 EIS1 上的 SHIPTO 事務.該事務查找客戶的送貨地址,準備 EIS 需要與送貨部門溝通的信息.返回的結構 OrderInfo 包含訂單 ID 號(用作跟蹤)、送貨成本和送貨地址信息.

  BillingInfo billCustomer(String customerId, OrderInfo orderInfo) 也使用 JCA CCI 調用 EIS1 上的 BILLTO 事務.該事務查詢客戶的信用卡號碼,並借記客戶的帳戶,記下訂單數量.返回的結構 BillingInfo 包含客戶的總成本,包括運輸費用和成本結構、該訂單的 ID 號(用作跟蹤和取消),以及客戶信息.

  void cancelOrder(OrderInfo orderInfo) 用 JCA CCI 調用運行在 EIS1 上的 RMVORD 事務來取消訂單.

  會話 bean CatalogService(請參閱 圖 1)充當 EIS2 的外觀.它定義了以下方法:

  double getItemPrice(String itemId) 用 JCA CCI 調用 EIS2 上的 ITMPRICE 事務.該事務將返回項目的最新價格信息.

  void updateStockInfo(String itemId, int numItems) 用 JCA CCI 調用 EIS2 上的 UPDSTOCK 事務.這個事務根據項目 ID 號,更新當前的庫存信息.輸入參數 numItems 可以是正的,也可以是負的.

  所有這 5 種方法和底層的 EIS 事務都運行在兩個不同系統上,它們必須作為一個單一的業務事務的組成部分來執行.在下面一節中,您將看到如何實現這一點.

  事務支持級別

  不同的 EIS 有不同的事務性行為.有些 EIS 根本不支持事務.有些 EIS 支持事務,但是不支持雙向提交(2PC)協議.這類事務被稱為支持本地事務.有些 EIS 既支持本地事務,又支持 2PC.這類事務被稱為支持分散式事務,或者全局事務.全局事務也被稱為 XA 事務,它們包含 XAResource 介面.

  清單 1 顯示了 CICSECI 資源適配器的 ra.xml 中的一小段,您要用這個適配器訪問 EIS1. 元素的 Localtransaction 值表明,這個資源適配器支持本地事務,但是不能參與全局事務.會話 bean OrderService 用這個資源適配器來訪問包含 EIS1 的 CRM 事務.

  清單 1. CICSECI 資源適配器的 ra.xml 描述符的代碼片段

1 <!DOCTYPE connector PUBLIC "-//Sun Microsystems, Inc.//DTD Connector 1.0//EN"
2 "http://java.sun.com/dtd/connector_1_0.dtd">
3
4 <connector>
5 <display-name>ECIResourceAdapter</display-name>

6 <description>CICS J2EE ECI Resource Adapter</description>
7 <vendor-name>IBM</vendor-name>
8 <spec-version>1.0</spec-version>
9 <eis-type>CICS</eis-type>
10 <version>5.0.0 </version>
11 <license>

12 <description> </description>
13 <license-required>true</license-required>
14 </license>
15 <resourceadapter>
16 <managedconnectionfactory-class>
17 com.ibm.connector2.cics.ECIManagedConnectionFactory</managedconnectionfactory-class>
18 <connectionfactory-interface>

19 javax.resource.cci.ConnectionFactory</connectionfactory-interface>
20 <connectionfactory-impl-class>
21 com.ibm.connector2.cics.ECIConnectionFactory</connectionfactory-impl-class>
22 <connection-interface>javax.resource.cci.Connection</connection-interface>
23 <connection-impl-class>com.ibm.connector2.cics.ECIConnection</connection-impl-class>
24 <transaction-support>LocalTransaction</transaction-support>

25

  清單 2 顯示了 IMS 資源適配器的 ra.xml 的代碼片斷,我用該適配器訪問 EIS 2.在該例中, 元素的值 XATransaction 表示資源適配器支持全局事務或分散式事務.

  清單 2. IMS 資源適配器的 ra.xml 描述符的代碼片斷

1 <!DOCTYPE connector PUBLIC "-//Sun Microsystems, Inc.//DTD Connector 1.0//EN"
2 "http://java.sun.com/dtd/connector_1_0.dtd">
3 <connector>
4 <display-name>IMS Connector for Java</display-name>
5 <description>J2EE Connector Architecture resource adapter for IMS
6 accessing IMS transactions using IMS Connect </description>
7 <vendor-name>IBM Corporation</vendor-name>

8 <spec-version>1.0</spec-version>
9 <eis-type>IMS</eis-type>
10 <version>1.2.6</version>
11 <license>
12 <description>IMS Connector for Java is a component of the IMS Connect product and as such
13 is not separately licensed. It requires an IMS Connect license.</description>
14 <license-required>true</license-required>

15 </license>
16 <resourceadapter>
17 <managedconnectionfactory-class>
18 com.ibm.connector2.ims.ico.IMSManagedConnectionFactory</managedconnectionfactory-class>
19 <connectionfactory-interface>
20 javax.resource.cci.ConnectionFactory</connectionfactory-interface>
21 <connectionfactory-impl-class>
22 com.ibm.connector2.ims.ico.IMSConnectionFactory</connectionfactory-impl-class>

23 <connection-interface>
24 javax.resource.cci.Connection</connection-interface>
25 <connection-impl-class>
26 com.ibm.connector2.ims.ico.IMSConnection</connection-impl-class>
27 <transaction-support>XATransaction</transaction-support>
28 <config-property>
29
30
31

  JCA 事務支持

  清單 1 和清單 2 中所示的資源適配器在其事務支持上有所區別.但是,在我的電子交易應用程序中,我需要把兩個不同系統上的事務協調起來.為了有助於實現這一點,JCA 事務合約定義了一套框架性介面,通過這套介面,應用程序伺服器 style="COLOR: #000000" href="http://server.it168.com/" target=_blank>伺服器和 EIS 可以協調事務.EJB 容器通過資源適配器實現的 ManagedConnection 介面與 EIS 對話,該介面表示了與 EIS 的實際連接. ManagedConnection 的實現通常要使用專有的庫,用 EIS 能夠理解的協議與 EIS 進行對話.

  如果資源適配器支持本地事務,那麼它還要實現 javax.resource.spi.LocalTransaction 介面.當 EJB 容器需要初始化事務的時候,它要調用 ManagedConnection 實現的實例上的 getLocalConnection() 方法.然後,它要調用 Localtransactionbegin()、 commit() 和 rollback() 方法來控制事務.

  如果資源適配器支持 XA 或全局事務,那麼它還要實現 javax.transaction.xa.XAResource 介面.每個符合 J2EE 規範的應用程序伺服器都有一個叫作事務管理器的內部組件.事務管理器實際是 javax.transaction.TransactionManager 介面的實現.這個組件幫助應用程序伺服器管理事務的邊界.事務管理器用 ManagedConnection 介面定義的 getXAResource() 方法得到 XAResource 的實例.事務管理器用 XAResource 介面定義的方法,在多個資源管理器之間協調 2PC 協議.

  事務界定策略

  JCA 提供了兩個處理事務的選項:程序性事務界定或聲明性事務界定.第一個選項要求您使用 Java 事務 API(JTA),顯式地為每個事務的 begin、 commit 和 rollback 操作編寫代碼.在這種情況下,事務界定代碼與實現業務邏輯的代碼混雜在一起.

  第二種方法是聲明性事務界定,它不包含任何額外的編碼工作.如果選擇這種方法,那麼 EJB 部署人員需要修改 bean 的部署描述符的設置,配置事務性行為.這樣,EJB 容器就會用這些部署描述符設置在合適的點上自動 begin、 commit 或 rollback 事務.在這種情況下,在 EJB 組件中實現的業務邏輯可以保持可移植性,不需要重寫 bean 實現,就能調整事務性行為.

  在多數情況下,聲明性事務界定是首選選項.程序性界定通常用在聲明性事務界定不夠靈活的情況下.在我的示例中,兩個 EIS 和它們對應的資源適配器有不同的事務支持級別.如果這些是惟一需要考慮的因素,那麼使用 2PC 協議的分散式事務應當是保證數據一致性和完整性的最佳方法.

  但是,為了讓事情更有趣,在我的示例中,只有 EIS2 支持分散式事務.為了使用 2PC,包含在事務中的所有系統都必須支持它.,我不能使用分散式事務,不得依靠本地事務才能保證在兩個系統之間更新的一致性.我需要對每個 EIS 的訪問進行分組,如果兩個 EIS 的其中之一失敗,那麼還需要手工取消對另一個 EIS 的修改.用來取消前面提交的變化的事務通常叫 補償事務.我將用程序性事務界定為手動更新提供更大的靈活性.

  程序性事務界定

  清單 3 顯示了會話 bean CustomerSession 中定義的方法 placeOrder 在應用程序中的實現.該方法用 JTA API 啟動控制 EIS1 和 EIS2 的訪問的本地事務.在 placeOrder 方法中,要訪問 EIS2,以獲得最新的價格信息.雖然在性質上,這個訪問是只讀的,但它仍然應當發生在事務的範圍中.這是,對於 EIS2 中價格信息的更新,可能是由其他應用程序持續進行的,而您需要保證看到的是一致提交的價格數據.請注意,出於簡便的原因,例外處理和業務邏輯已經做了簡化處理.

  清單 3. CustomerSession EJB 的 placeOrder() 方法(為了簡便起見,對錯誤處理進行了簡化)

1 public OrderInfo placeOrder(ItemInfo itemInfo, CustomerInfo custInfo)
2 throws OrderException {
3 // Get a reference to the UserTransaction.
4 // Initialize variables.
5 BillingInfo billingInfo = null;
6 OrderInfo orderInfo = null;
7 double itemPrice = 0.0;
8 UserTransaction ut = null;

9 try
10 {
11 InitialContext ic = new InitialContext();
12 ut = (UserTransaction)ic.lookup("jta/UserTransaction");
13 }
14 catch (Exception e)
15 {
16 throw new OrderException(e.getMessage());
17 }
18 // Look up latest pricing information in EIS2 including customer discount.
19 try
20 {
21 ut.begin();

22 itemPrice = catalogService.getItemPrice(itemInfo.getItemId(),
23 custInfo.getCustomerId());
24 ut.commit();
25 }
26 catch ( Exception e)
27 {
28 try
29 {
30 ut.rollback();
31 }
32 catch (Exception ex)
33 {
34 // Rollback failed.
35 // Log the error here, nothing to recover.
36 }
37 // Throw exception back to the UI tier, nothing to compensate yet.

38 throw new OrderException(e.getMessage());
39 }
40 itemInfo.setItemPrice(itemPrice);
41 // Update EIS1 - local transaction
42 try
43 {
44 ut.begin();
45 billingInfo = orderService.billCustomer( custInfo.getId(), itemInfo );
46 orderInfo = orderService.addOrder( custInfo.getId(), itemInfo );
47 ut.commit();
48 }
49 catch ( Exception e)
50 {
51 // Nothing to compensate in EIS2 yet.

52
53 try
54 {
55 ut.rollback();
56 }
57 catch (Exception ex)
58 {
59 // Rollback failed -- log the error.
60 // Additional checks and error handling to ensure consistency in EIS1.
61 }
62 throw new OrderException(e.getMessage());
63
64 }
65 // Update EIS2.
66 try

67 {
68 ut.begin();
69 catalogService.updateStockInfo(orderInfo.getItemId(), orderInfo.getItemNumber());
70 ut.commit();
71 }
72 catch( Exception e )
73 {
74 // Roll back the original transaction to EIS2.
75 try
76 {
77 ut.rollback();
78 }
79 catch (Exception ex)
80 {
81 // Rollback failed - log the error.
82 // Additional checks and error handling.
83 // Do not exit the method yet.

84 }
85 // Compensate changes to EIS1 as a single one-phase transaction.
86 try
87 {
88 ut.begin();
89 orderService.cancelOrder( orderInfo );
90 orderService.cancelCharge( billingInfo );
91 ut.commit();
92 }
93 catch ( Exception ex)
94 {
95 // Compensation failed, log error
96 try
97 {
98 ut.rollback();
99 }
100 catch (Exception exx)

101 {
102 // Rollback failed
103 // Log error
104 }
105 throw new OrderException(ex.getMessage());
106 }
107 // Throw exception back to the UI tier
108 throw new OrderException(e.getMessage());
109 }
110 return orderInfo;
111 }
112
113

  下一步是執行對 EIS1 的兩個更新:一個是對訂單系統更新,另一個是對記帳系統進行更新.可以在本地事務的範圍內執行這些更新.如果其中一個更新失敗,那麼可以向 UI 層重新拋出異常,退出該方法,不再更新 EIS2.UI 層則需要告訴用戶事務失敗了,並請求用戶指示如何處理情況.用戶可能選擇重試或者退出應用程序.這兩項操作都是運行在同一事務中,而前面對 EIS2 的訪問是只讀的,在這一段中需要補償事務.

  您要做的最后一件事,就是更新 EIS2 中的可用庫存數據.這項操作還是在本地事務的範圍中執行的,但是,本地事務是與已經完成的 EIS1 事務不同的事務.要使用程序性事務界定,更新 EIS2 操作的失敗,會造成取消 EIS1 中已經提交的修改.這就是 catch 塊中包含對 EIS1 事務的調用的原因,為了取消對訂單和記帳系統的更新.

  值得注意的是,對於 2PC 的分散式事務,程序性事務界定的方法不是完美的替代品.如果補償事務本身失敗,那麼系統就會處於不一致狀態.在現實的應用程序中,需要額外的異常處理來處理這類情況.例如,在補償事務失敗的情況下,您可以向系統管理員發送通知,或者用消息技術稍後重試這些事務.

  EJB 部署描述符設置

  在基於 EJB 的解決方案中,EJB 部署人員必須通過配置 EJB 部署描述符設置,告訴應用程序伺服器如何處理事務界定.為應用程序選擇正確的部署描述符設置的第一步,就是評估它的需求.在這裡,該需求是示例 JCA 實現的需求:

  EJB 組件 CustomerSession 的部署描述符必須指示您,您將用通過編程實現的(也就是由 bean 管理的)事務界定 ( TX_BEAN_MANAGED).

  會話 bean OrderService 和 CatalogService 的部署描述符應當使用聲明性(也就是由容器管理的界定)事務界定,這些 bean 上的方法應當一直在事務的上下文環境內執行.

  會話 ben OrderService 和 CatalogService 提供對底層 EIS 事務的訪問.這些 bean 上的方法由會話 bean CustomerSession 調用,它們實現了應用程序背後的處理邏輯,其他面向處理的組件也可以調用它們.

  CustomerSession bean 應當在調用 OrderService 上的方法之前啟動一個事務,要由這個事務來包裝在 EIS1 上進行的多個操作.要在 EIS2 上進行兩個操作,每一個操作都可以作為獨立的事務執行(無論哪個操作都不能作為包裝所有 4 個操作的全局操作的一部分), 不管是程序性界定,還是聲明性界定,都可以用來調用 CatalogService 方法.(請注意,我傾向於由 CustomerSession bean 負責為所有所操作控制事務邊界.)

  根據這些需求,可以為 CatalogService 和 OrderService bean 使用 TX_REQUIRED 部署描述符設置.該設置可以保證 bean 的方法可以加入已經進行的事務中,或者在需要的時候啟動新的事務.請注意,使用 TX_REQUIRED 時,如果調用代碼沒有啟動事務,那麼每個調用,包括對 EIS 的那些調用,都將作為獨立的事務發生,而這可能並不是您想要的.

  一個替代的辦法可能是強制所有實現處理邏輯的組件(例如, CustomerSession bean) 執行事務界定.通過對會話 bean OrderService 和 CatalogService 使用 TX_MANDATORY 部署描述符,可以實現這個方法,它能保證應用程序執行預期的事務性行為.如果選擇這種方法,那麼在調用組件時,需要在調用 EIS 代理的方法之前啟動一個事務,否則就會拋出異常.這種方法的不足之處是,它要求只需要單一方法事務的組件也要負責控制事務的邊界.

  指定事務隔離級別

  J2EE 規範定義了用來為具體 EJB 方法指定事務隔離級別的部署描述符屬性.例如,EJB 部署人員可以設置會話 bean CatalogService 的方法 getItemPrice() 的事務隔離級別,使其在 TRANSACTION_READ_COMMITTED 隔離級別中運行,以確保在事務期間,只讀取提交的價格數據.通過修改隔離級別,J2EE 開發人員或部署人員可以在性能的約束下平衡數據完整性,從而對應用程序進行調整.

  但是,不同的 EIS 有不同的事務性能力,因此會以不同的方式對事務隔離設置作出反應.在這種情況下,改變 EIS 代理的事務隔離設置,可能不會有什麼區別, ECI 和 XCF 通信協議不會把該信息傳遞到後端.運行在這些系統上的 COBOL 事務會用這些平台上特定的技術解決事務性隔離問題.

  選擇解決方案

  一般來說,要確定具體目標 EIS 會如何響應事務隔離設置,需要參考該 EIS 的文檔,對於用來提供連接性的資源適配器,也是如此.雖然示例中使用的資源適配器不響應 J2EE 事務性隔離級別設置的變化,但是某些圍繞關係資料庫系統構建的 EIS 確實可能會受到影響.重要的是,要理解由於這些變化在具體 EIS 中可能帶來的正確行為,這對 EIS 和應用程序的性能指標可能會有嚴重衝擊.在更壞的情況下,底層數據的完整性可能被破壞.進行 J2EE 項目的開發人員應當諮詢 EIS 管理員,以確保事務性管理隔離遵循的是正確的策略.

  結束語

  Java 連接器架構規範為 J2EE 開發人員構建包含傳統系統的事務性 J2EE 程序提供了方便的解決方案.JCA 允許在集成現有的 EIS 系統的同時,維持電子商務所需要的正確的事務性語義.

  您通常需要解決的關鍵問題是:不得不處理不同的資源適配器,提供不同級別事務支持.在許多情況下,有可能無法依賴底層的事務管理器,在受影響的系統之間協調分散式事務.在有些情況下,即使有可能,分散式事務也可能由於與雙向提交協議有關的性能問題而變得不適用.

  在這種情況下,可能需要依賴補償事務邏輯和程序性事務界定.這種方法也有它的不足之處.補償事務本身可能會失敗,給系統留下一個不一致的狀態.

  另一個需要注意的問題是,JCA 規範沒有規定資源適配器應當如何處理 EJB 事務隔離級別,必須解決這個問題.有些資源適配器只是忽略這些設置,目標 EIS 的具體通信協議不能把這個信息傳遞給後端,或者是目標 EIS 的事務模型不提供等價的概念.在其他情況下,對 J2EE 事務隔離級別的修改可能會嚴重影響 EIS 的性能.為了確保正確操作,您應當仔細研究資源適配器的文檔,如果這方面的內容介紹得不夠詳細,那麼您還應該諮詢廠家.


[火星人 ] 理解 JCA 事務已經有709次圍觀

http://coctec.com/docs/java/show-post-61958.html