9.5. 오류 처리 및 롤백
트랜잭션 경로에서 표준 Apache Camel 오류 처리 기술을 사용할 수 있지만 예외와 트랜잭션 요약 간의 상호 작용을 이해하는 것이 중요합니다. 특히 throw된 예외로 인해 일반적으로 트랜잭션 롤백이 발생하는 것을 고려해야 합니다. 다음 항목을 참조하십시오.
9.5.1. 트랜잭션을 롤백하는 방법
다음 방법 중 하나를 사용하여 트랜잭션을 롤백할 수 있습니다.
9.5.1.1. 런타임 예외를 사용하여 롤백 트리거
Spring 트랜잭션을 롤백하는 가장 일반적인 방법은 런타임(확인되지 않음) 예외를 throw하는 것입니다. 즉, 예외는 java.lang.RuntimeException
의 인스턴스 또는 하위 클래스입니다. java.lang.Error
유형의 Java 오류도 트랜잭션 롤백을 트리거합니다. 반면 검사된 예외는 롤백을 트리거하지 않습니다.
다음 그림은 Java 오류 및 예외가 트랜잭션에 미치는 영향을 요약한 것으로, 롤백을 트리거하는 클래스는 회색으로 표시됩니다.
Spring 프레임워크는 롤백을 트리거하거나 트리거하지 않아야 하는 예외를 지정할 수 있는 XML 주석 시스템도 제공합니다. 자세한 내용은 Spring Reference Guide의 "Rolling back"를 참조하십시오.
트랜잭션 내에서 런타임 예외가 처리되는 경우, 즉 예외가 트랜잭션 분리를 수행하는 코드로 대체될 가능성이 있기 전에 트랜잭션이 롤백되지 않습니다. 자세한 내용은 9.5.2절. “dead letter queue를 정의하는 방법”의 내용을 참조하십시오.
9.5.1.2. rollback()
DSL 명령 사용
트랜잭션된 경로의 중간에서 롤백을 트리거하려면 org.apache.camel.RollbackExchangeException
예외를 throw하는 rollback()
DSL 명령을 호출하여 이 작업을 수행할 수 있습니다. 즉, rollback()
명령은 런타임 예외를 발생시켜 롤백을 트리거하는 표준 접근 방식을 사용합니다.
예를 들어 계정 서비스 애플리케이션에서 수익 이전 크기에 대한 절대 제한이 있어야 한다고 가정합니다. 다음 예제의 코드를 사용하여 양이 100을 초과하면 롤백을 트리거할 수 있습니다.
from("file:src/data?noop=true") .transacted() .bean("accountService","credit") .choice().when(xpath("/transaction/transfer[amount > 100]")) .rollback() .otherwise() .to("direct:txsmall"); from("direct:txsmall") .bean("accountService","debit") .bean("accountService","dumpTable") .to("file:target/messages/small");
이전 경로에서 롤백을 트리거하면 무한 루프에 갇히게 됩니다. 그 이유는 rollback()
에서 throw한 RollbackExchangeException
예외가 경로 시작 시 파일
끝점으로 다시 전파되기 때문입니다. File 구성 요소에는 예외가 throw된 모든 교환을 다시 보낼 수 있는 기본 제공 안정성 기능이 있습니다. 물론 다시 전송하면 교환이 다른 롤백을 트리거하여 무한 루프가 발생합니다. 다음 예제에서는 무한 루프를 방지하는 방법을 보여줍니다.
9.5.1.3. markRollbackOnly()
DSL 명령 사용
markRollbackOnly()
DSL 명령을 사용하면 예외를 발생하지 않고 현재 트랜잭션이 롤백되도록 할 수 있습니다. 이 기능은 예외에 9.5.1.2절. “rollback()
DSL 명령 사용” 의 예와 같이 원하지 않는 부작용을 발생시킬 때 유용할 수 있습니다.
다음 예제에서는 rollback()
명령을 markRollbackOnly()
명령으로 교체하여 9.5.1.2절. “rollback()
DSL 명령 사용” 의 예제를 수정하는 방법을 보여줍니다. 이 버전의 경로는 무한 루프의 문제를 해결합니다. 이 경우 수익 이전 양이 100을 초과하면 현재 트랜잭션이 롤백되지만 예외는 발생하지 않습니다. 파일 끝점에서 예외를 수신하지 않으므로 교환을 재시도하지 않고 실패한 트랜잭션이 자동으로 삭제됩니다.
다음 코드는 markRollbackOnly()
명령을 사용하여 예외를 롤백합니다.
from("file:src/data?noop=true") .transacted() .bean("accountService","credit") .choice().when(xpath("/transaction/transfer[amount > 100]")) .markRollbackOnly() .otherwise() .to("direct:txsmall"); ...
그러나 앞의 경로 구현에는 적합하지 않습니다. 경로가 트랜잭션을 깔끔하게 롤백하고 일관된 상태의 데이터베이스를 그대로 롤백하고 무한 루프 처리 문제를 방지하지만 실패한 트랜잭션에 대한 레코드를 유지하지 않습니다. 실제 애플리케이션에서는 일반적으로 실패한 트랜잭션을 추적하려고 합니다. 예를 들어 트랜잭션이 성공하지 못한 이유를 설명하기 위해 관련 고객에게 문자를 작성하고자 할 수 있습니다. 실패한 트랜잭션을 추적하는 편리한 방법은 경로에 dead-letter queue를 추가하는 것입니다.
9.5.2. dead letter queue를 정의하는 방법
실패한 트랜잭션을 추적하기 위해 관련 교환 개체를 dead-letter queue로 전환할 수 있는 onException()
절을 정의할 수 있습니다. 그러나 트랜잭션 컨텍스트에서 사용하는 경우 예외 처리와 트랜잭션 처리 간의 잠재적인 상호 작용으로 인해 onException()
절을 정의하는 방법에 대해 주의해야 합니다. 다음 예제에서는 rethrown 예외를 억제해야 한다고 가정하면 onException()
절을 정의하는 올바른 방법을 보여줍니다.
// Java import org.apache.camel.builder.RouteBuilder; public class MyRouteBuilder extends RouteBuilder { ... public void configure() { onException(IllegalArgumentException.class) .maximumRedeliveries(1) .handled(true) .to("file:target/messages?fileName=deadLetters.xml&fileExist=Append") .markRollbackOnly(); // NB: Must come *after* the dead letter endpoint. from("file:src/data?noop=true") .transacted() .bean("accountService","credit") .bean("accountService","debit") .bean("accountService","dumpTable") .to("file:target/messages"); } }
이전 예에서 onException()
은 IllegalArgumentException
예외를 catch하고 잘못된 교환을 dead letter file dead letter file, deadLet Cryostat.xml
로 전송하도록 구성되어 있습니다. 물론 이 정의를 변경하여 애플리케이션에서 발생하는 모든 종류의 예외를 파악할 수 있습니다. 예외 다시 화살표 동작 및 트랜잭션 롤백 동작은 onException()
절의 다음과 같은 특수 설정으로 제어됩니다.
-
handled(true)
- rethrown 예외를 비활성화합니다. 이 특정 예에서 rethrown 예외는 파일 엔드포인트로 다시 전파될 때 무한 루프를 트리거하기 때문에 바람직하지 않습니다. 9.5.1.3절. “markRollbackOnly()
DSL 명령 사용”을 참조하십시오. 그러나 경로 시작 시 끝점이 재시도 기능을 구현하지 않는 경우 예외를 다시 시작하는 것이 허용되는 경우도 있습니다. -
markRollbackOnly()
- 예외를 throw하지 않고 롤백의 현재 트랜잭션을 표시합니다. 교환을 dead letter queue로 라우팅하는to()
명령 뒤에 이 DSL 명령을 삽입하는 것이 중요합니다. 그렇지 않으면markRollbackOnly()
가 처리 체인을 중단하기 때문에 교환은 dead letter 큐에 도달하지 않습니다.
9.5.3. 트랜잭션 관련 예외 처리
onException()
을 사용하는 대신 트랜잭션 경로에서 예외를 처리하는 간단한 방법은 경로 주위에 doTry()
및 doCatch()
절을 사용하는 것입니다. 예를 들어 다음 코드는 무한 루프에 갇혀 있을 위험 없이 트랜잭션 경로에서 IllegalArgumentException
을 catch하고 처리하는 방법을 보여줍니다.
// Java import org.apache.camel.builder.RouteBuilder; public class MyRouteBuilder extends RouteBuilder { ... public void configure() { from("file:src/data?noop=true") .doTry() .to("direct:split") .doCatch(IllegalArgumentException.class) .to("file:target/messages?fileName=deadLetters.xml&fileExist=Append") .end(); from("direct:split") .transacted() .bean("accountService","credit") .bean("accountService","debit") .bean("accountService","dumpTable") .to("file:target/messages"); } }
이 예에서 경로는 두 세그먼트로 나뉩니다. 첫 번째 세그먼트( 파일:src/data
끝점)는 들어오는 교환을 수신하고 doTry()
및 doCatch()
를 사용하여 예외 처리를 수행합니다. 두 번째 세그먼트( 직접:split
끝점)는 모든 트랜잭션 작업을 수행합니다. 이 트랜잭션 세그먼트 내에서 예외가 발생하면 먼저 transacted()
명령으로 전파되므로 현재 트랜잭션이 롤백되고 첫 번째 경로 세그먼트의 doCatch()
절에 의해 catch됩니다. doCatch()
절은 예외를 다시 가져오지 않으므로 파일 끝점에서 재시도를 수행하지 않고 무한 루프를 방지할 수 있습니다.