簡體   English   中英

如何在Spring和GlassFish 5中進行分布式事務XA?

[英]How to do Distributed Transactions XA in Spring and GlassFish 5?

我正在嘗試創建一個包含兩個REST Web服務的事務,其數據源指向相同的數據庫。 名為1的第一個服務使用Spring RestTemplate調用另一個名為2 Web服務。

為了實現事務,我使用的是JNDI連接池,MySql JDBC驅動程序(版本5.1.35),JTA,XA,Spring和GlassFish 5 AppServer。

現在,我已經在Spring項目中下載了maven依賴項,使用JtaTransactionManager定義了一個配置類,並在application.yml文件中配置了數據源和JTA屬性,如下面的代碼所示:

配置類:

@Configuration
@EnableTransactionManagement
public class Transacciones {

    @Bean
     public PlatformTransactionManager platformTransactionManager(){ 
        return new JtaTransactionManager();
    }

}

application.yml文件

spring:
  datasource:
    jndi-name: jdbc/Prueba  
    driver-class-name: com.mysql.jdbc.Driver

  jta:
    enabled: true                               

我在GlassFish 5中配置了JNDI數據源,使用名為pruebaXAjavax.sql.XADataSource數據源在“Connections pools”頁面中定義名為jdbc/Prueba的“JDBC資源”:

GlassFish,連接池

在Web服務1的控制層中,該方法使用Spring Framework的RestTemplate類調用服務2

服務1代碼:

@RestController
@RequestMapping("/servicio")
@EnableTransactionManagement
public class a {

    @Autowired
    private JdbcTemplate objJdbcTemplate;


    @Transactional(rollbackFor = RuntimeException.class)
    @GetMapping("/1")
    public Integer getValor(){
        try{
            int numero;
            int n=50;
            RestTemplate restTemplate = new RestTemplate();

            Integer intRes1;
            Integer intRes2;
            numero = (int) (Math.random() * n) + 1;

            intRes2 = restTemplate.postForObject("http://localhost:8080/servicio2-1.0-SNAPSHOT/servicio/2",numero,Integer.class);

            intRes1=objJdbcTemplate.update("INSERT INTO A VALUES(" +numero + ")");

            return numero;
        }catch(Exception e){
            throw new RuntimeException(e);
        }
    }
}

服務2代碼:

@RestController
@RequestMapping("/servicio")
public class a {

    @Autowired
    private JdbcTemplate objJdbcTemplate;

    @Transactional(rollbackFor = RuntimeException.class)
    @PostMapping("/2")
    public Integer getValor(@RequestBody Integer intNum){
        try{
            Integer intRes;

            intRes=objJdbcTemplate.update("INSERT INTO B VALUES(" + intNum + ")");
            return intRes;
        }catch(Exception e){
            throw new RuntimeException(e);
        }
    }
}

如果兩個服務沒有錯誤,則沒有問題。 但是,當服務1下降時,服務2不知道錯誤並且不進行回滾。

我不知道是否需要在GlassFish 5或Spring程序中配置另一個功能/選項。

我已經讀過,在Spring中只需要一個JtaTransactionManager bean,框架執行與配置和使用JTA事務相關的所有工作。 春天和JTA

JTA

現在,如果您仍然需要使用JTA,至少可以選擇您的選擇。 有兩種常見的情況:在重量級應用程序服務器中使用JTA(具有綁定到JavaEE服務器的所有令人討厭的缺點),或使用獨立的JTA實現。 Spring通過名為JtaTransactionManager的PlatformTransactionManager實現為基於JTA的全局事務實現提供支持。 如果在JavaEE應用程序服務器上使用它,它將自動從JNDI中找到正確的javax.transaction.UserTransaction引用。 此外,它將嘗試在9個不同的應用程序服務器中查找特定於容器的javax.transaction.TransactionManager引用,以獲取更高級的用例,例如事務暫停。 在幕后,Spring加載了不同的JtaTransactionManager子類,以便在可用時利用不同服務器中的特定額外功能,例如:WebLogicJtaTransactionManager,WebSphereUowTransactionManager和OC4JJtaTransactionManager。

因此,如果您在Java EE應用程序服務器中,並且無法轉義,但想要使用Spring的JTA支持,那么您可以使用以下命名空間配置支持來正確(並自動)生成JtaTransactionManager:

或者,您可以根據需要注冊JtaTransactionManager bean實例,不帶構造函數參數,如下所示:

@Bean public PlatformTransactionManager platformTransactionManager(){

 return new JtaTransactionManager(); } Either way, the end result in a JavaEE application server is that you can now use JTA to manage 

感謝Spring,你的交易是統一的。

謝謝你的幫助和時間。

如何進行分布式事務XA?

如果首先調用REST或Web服務,然后再調用另一個,則兩個操作都不會成為事務的一部分。 為了形成交易,這些操作必須“開始”交易或“加入”現有交易。 要執行該事務,您的程序必須與事務監視器(TM)交互,例如AT&T / Oracle Tuxedo (80年代發布), X / Open XA標准 (90年代發布)和基於JTA的系統。

請注意基於TM的事務如何工作:

  • 使用XA數據源的事務基本上是一個在兩個不同數據庫上調用數據庫操作的程序。 相同的程序(例如,調用一個或多個bean中的方法)啟動事務,對數據庫執行某些操作並對另一個數據庫執行其他操作。 如果其中一個操作失敗,則不執行其他操作或回滾操作。

  • 使用XA數據源和支持JTA的組件的事務基本上是一個程序,它將一個或多個數據庫上的操作與其他啟用事務的操作相結合。 例如,您可以將數據庫上的操作與內容存儲庫或基於網絡的文件系統上的操作相結合。 您可以定義在數據庫操作失敗時不對文件系統執行或回滾操作的事務。 通過在事務監視器中定義操作和補償,可以將非事務性應用程序(如基於COBOL的程序)集成到事務中。

  • 集成Web服務的事務需要特殊處理。 例如,有一個Web服務事務的提議,例如WS-TransactionWS-Coordination規范。 有一個協調器 ,它像事務監視器一樣工作。 軟件必須調用協調器才能啟動事務。 事務中的每個參與者報告每個操作是成功還是失敗。 協調器根據結果調用其他操作或調用補償。

如今,有一些軟件架構的提議不依賴於基於TM的事務。 基於CQRSEvent Sourcing設計使用Sagas設計模式實現事務。 如果您對定義調用兩個REST服務的類事務操作感興趣,可以考慮避免使用XA / JTA並編寫Sagas。

如何在Spring和GlassFish 5中進行分布式事務XA?

您可以在Internet上查看許多教程。 例如,

  • 一個教程 ,向您展示三個用例:一個更新兩個數據庫,一個組合數據庫操作和傳出JMS消息,另一個組合傳入JMS消息和數據庫操作。
  • 一個視頻,描述了如何使用JTA在Spring中管理分布式事務
  • 以及Spring Framework的文檔

如果兩個服務沒有錯誤,則沒有問題。 但是,當服務1下降時,服務2不知道錯誤並且不進行回滾。

這是正確的行為。 調用REST / Web服務時,該REST / Web服務執行的操作不會加入事務。 如果調用服務失敗,則調用的服務將不會注意到它,也不會回滾它們在數據庫中的操作。

我不知道是否需要在GlassFish 5或Spring程序中配置另一個功能/選項。

不需要。您只需配置XA數據源。 Spring將使用您的配置類和注釋將應用程序在這些數據源上執行的操作自動連接到事務中。 例如,如果您有一個bean調用多個方法來對一個或多個XA數據源執行操作,那么這些操作將加入到一個事務中。

但是,當您在另一個應用程序中調用REST / webservice中的方法時,該REST / Web服務執行的數據庫操作將不會加入該事務。

其余的Web服務(基於http)本質上是非事務性的(它們是基於http的)。 您已經使每個方法/操作都是事務性的,但它們不共享資源之間的任何狀態(休息操作)。 通常 - 您可以通過數據庫或消息傳遞進行XA事務,而不是通過http調用。

intRes2 = restTemplate.postForObject("http://localhost:8080/servicio2-1.0-
SNAPSHOT/servicio/2",numero,Integer.class);

調用遠程Web服務沒有任何事務上下文。 如果需要維護服務之間的事務,請將secord服務作為EJB(或作為注入的托管bean)調用

基本上:使用基於http的休息服務 - 忘記它們之間的任何交易。 協議(HTTP)不是為此而構建的。

我唯一看到事務性的是具有WS-RM擴展的SOAP(帶有可靠消息傳遞的SOAP)。但是它設置起來不是很容易(閱讀:當你不知道什么是它時,它可能是噩夢你正在做)並不是所有的WS框架都支持它。

當您真正需要在Web服務之間可靠交付時,有一種方法。 通常用於實現有保證的交付的是具有存儲轉發模式的冪等服務( https://en.m.wikipedia.org/wiki/Idempotence )的消息傳遞。 簡單來說 - 服務1將JMS消息存儲到隊列中,並且存在調用服務2的處理器(MDB)。(是的,調用遠程Web服務可能會發生服務2將多次接收消息。不約束是一個如何處理它。)

http://www.atomikos.com支持跨REST服務的事務

干杯

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM