복붙노트

[SPRING] Spring과 GlassFish 5에서 분산 트랜잭션 XA를 수행하는 방법은 무엇입니까?

SPRING

Spring과 GlassFish 5에서 분산 트랜잭션 XA를 수행하는 방법은 무엇입니까?

두 개의 REST 웹 서비스를 포함하는 트랜잭션을 만들려고합니다. 데이터 소스는 동일한 데이터베이스를 가리 킵니다. 첫 번째 서비스 인 1은 Spring RestTemplate을 사용하여 2라는 다른 웹 서비스를 호출합니다.

트랜잭션을 구현하기 위해 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();
    }

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

  jta:
    enabled: true                               

pruebaXA라는 javax.sql.XADataSourcedatasource를 사용하여 "Connections 풀"페이지에서 jdbc / Prueba라는 "JDBC 자원"을 정의하는 GlassFish 5에서 JNDI 데이터 소스를 구성했습니다.

웹 서비스 1의 제어 계층에서이 메서드는 Spring Framework의 RestTemplate 클래스를 사용하여 서비스 2를 호출합니다.

@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);
        }
    }
}
@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 빈 만 필요하다는 것을 읽었고 프레임 워크는 JTA 트랜잭션을 구성하고 사용하는 것과 관련된 모든 작업을 수행한다. Spring과 JTA

도와 주셔서 감사합니다.

해결법

  1. ==============================

    1.먼저 REST 또는 웹 서비스를 호출 한 다음 다른 서비스를 호출하면 두 작업 모두 트랜잭션의 일부가되지 않습니다. 트랜잭션을 형성하기 위해 이러한 조작은 트랜잭션을 "시작"하거나 기존 트랜잭션을 "결합"해야합니다. 트랜잭션을 실행하려면 프로그램이 AT & T / Oracle Tuxedo (80 년대에 릴리스 됨), X / Open XA 표준 (90 년대에 릴리스 됨) 및 JTA 기반 시스템과 같이 트랜잭션 모니터 (TM)와 상호 작용해야합니다 .

    먼저 REST 또는 웹 서비스를 호출 한 다음 다른 서비스를 호출하면 두 작업 모두 트랜잭션의 일부가되지 않습니다. 트랜잭션을 형성하기 위해 이러한 조작은 트랜잭션을 "시작"하거나 기존 트랜잭션을 "결합"해야합니다. 트랜잭션을 실행하려면 프로그램이 AT & T / Oracle Tuxedo (80 년대에 릴리스 됨), X / Open XA 표준 (90 년대에 릴리스 됨) 및 JTA 기반 시스템과 같이 트랜잭션 모니터 (TM)와 상호 작용해야합니다 .

    TM 기반 트랜잭션의 작동 방식에 유의하십시오.

    요즘에는 TM 기반 트랜잭션에 의존하지 않는 소프트웨어 아키텍처에 대한 몇 가지 제안이 있습니다. CQRS 및 Event Sourcing을 기반으로 한 설계는 Sagas 디자인 패턴을 사용하여 트랜잭션을 구현합니다. 두 개의 REST 서비스를 호출하는 트랜잭션과 유사한 연산을 정의하는 데 관심이있는 경우 XA / JTA를 피하고 Sagas를 프로그래밍 할 수 있습니다.

    인터넷에서 많은 자습서를 확인할 수 있습니다. 예를 들어,

    그것이 올바른 행동입니다. REST / webservice를 호출 할 때 해당 REST / webservice가 수행하는 작업은 트랜잭션에 참여하지 않습니다. 호출 서비스가 실패하면 호출 된 서비스는이를 인식하지 않고 데이터베이스에서 해당 조작을 롤백하지 않습니다.

    아닙니다. XA-datasources 만 구성하면됩니다. Spring은 구성 클래스와 주석을 사용하여 이러한 데이터 소스에서 애플리케이션이 수행 한 작업을 트랜잭션에 자동으로 결합합니다. 예를 들어, 하나 이상의 XA-datasources에서 작업을 수행하는 여러 메소드를 호출하는 Bean이있는 경우 이러한 작업은 트랜잭션으로 결합됩니다.

    그러나 다른 응용 프로그램의 REST / webservice에서 메소드를 호출하면 해당 REST / webservice가 수행하는 데이터베이스 작업은 트랜잭션에 참여하지 않습니다.

  2. ==============================

    2.나머지 웹 서비스 (http 기반)는 본질적으로 비 트랜잭션 (http 기반)입니다. 당신은 각 메소드 / 연산을 트랜잭션으로 만들었지 만, 자원들 사이에 어떤 상태도 공유하지 않습니다 (rest 연산). 일반적으로 - http 호출을 통하지 않고 데이터베이스 또는 메시징을 통해 XA 트랜잭션을 수행 할 수 있습니다.

    나머지 웹 서비스 (http 기반)는 본질적으로 비 트랜잭션 (http 기반)입니다. 당신은 각 메소드 / 연산을 트랜잭션으로 만들었지 만, 자원들 사이에 어떤 상태도 공유하지 않습니다 (rest 연산). 일반적으로 - http 호출을 통하지 않고 데이터베이스 또는 메시징을 통해 XA 트랜잭션을 수행 할 수 있습니다.

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

    원격 웹 서비스 호출은 트랜잭션 컨텍스트없이 수행됩니다. 서비스 간 트랜잭션을 유지해야하는 경우 EJB (또는 주입 된 관리 Bean)로 secord 서비스를 호출하십시오.

    기본적으로 : http 기반 rest 서비스 사용 - 그들 사이의 모든 트랜잭션을 잊어 버려. 프로토콜 (HTTP)은이를 위해 만들어지지 않았습니다.

    내가 트랜잭션으로 본 유일한 것은 WS-RM 확장 (신뢰할 수있는 메시징을 사용하는 SOAP)을 사용하는 SOAP입니다. 그러나 설정하기가 쉽지 않습니다 (읽기 : 악몽 일 수 있습니다. 당신은) 모든 WS 프레임 워크가 그것을 지원하는 것은 아닙니다.

    웹 서비스간에 실제로 안정적인 전달이 필요한 경우 방법이 있습니다. 안심할 수있는 전달을 위해 자주 사용되는 것은 저장 및 전달 패턴을 가진 멱등 원이 아닌 서비스 (https://en.m.wikipedia.org/wiki/Idempotence)와의 메시징입니다. 간단히 말하면, 서비스 1은 대기열에 JMS 메시지를 저장하고 서비스 2를 호출하는 프로세서 (MDB)가 있습니다 (예, 원격 웹 서비스를 호출하면 서비스 2가 메시지를 여러 번 수신함). 방법을 다루는 방법.)

  3. ==============================

    3.REST 서비스를 통한 트랜잭션은 http://www.atomikos.com에서 지원합니다.

    REST 서비스를 통한 트랜잭션은 http://www.atomikos.com에서 지원합니다.

    건배

  4. from https://stackoverflow.com/questions/46008251/how-to-do-distributed-transactions-xa-in-spring-and-glassfish-5 by cc-by-sa and MIT license