제5장 JEUS MQ 장애 극복

내용 목차

5.1. 개요
5.2. 서버 장애 극복
5.2.1. 네트워크 구성
5.2.2. Connection Factory 설정
5.2.3. Persistence Store 설정
5.2.4. 자동 복원(Fail-Back)
5.3. 클라이언트 장애 극복
5.3.1. 재연결
5.3.2. Connection Factory 재사용
5.3.3. Destination 재사용
5.3.4. 응답 대기시간
5.3.5. 커넥션 복구
5.3.6. 세션 복구
5.3.7. 메시지 전송 복구
5.3.8. 메시지 수신 복구
5.3.9. 메시지 유실 방지와 트랜잭션

본 장에서는 JEUS MQ에서 서버나 네트워크에 장애가 발생해서 더 이상 메시징 서비스를 할 수 없을 때 JMS 클라이언트가 다시 연결을 맺고 클라이언트 상태를 복구하는 방법에 대해서 설명한다. 또한 JMS 클라이언트의 복구를 위해서 필요한 서버 구성과 서버 장애 극복에 대해서 설명한다.

JEUS MQ에서의 장애 극복이란 장애가 발생했을 때 클라이언트 애플리케이션이 자동으로 연결을 다시 맺고 클라이언트의 모든 상태를 장애가 발생하기 이전으로 복구하는 것을 의미한다.

장애가 발생하는 원인은 다음과 같이 크게 2가지로 나눌 수 있다.

  • 네트워크 장애

    네트워크 장애는 JEUS MQ 서버와 클라이언트 사이의 네트워크에 문제가 발생해서 더 이상 통신을 할 수 없는 상태를 의미한다.

    네트워크 장애는 일시적인 장애일 수도 있고 서버가 다운되거나 네트워크가 완전히 사용 불가능할 경우도 포함된다. 네트워크 장애가 발생하면 JEUS MQ 클라이언트들은 네트워크에 장애가 발생한 서버나 해당 서버와 클러스터링 관계에 있는 다른 서버에 재연결을 시도하고 재연결이 완료되면 클라이언트의 상태를 자동 복구하여 서비스를 계속하게 된다.

  • 서버 장애

    서버 장애는 네트워크 장애를 제외하고 서버에 발생하는 모든 장애를 포함한다. 일반적으로 메시징 데이터를 저장하는 디스크나 데이터베이스 작업을 수행할 때 오류가 발생하거나 메모리 부족 등이 이에 해당한다. 현재 서비스 중인 서버에 장애가 발생하면 대기 중이던 백업 서버는 이전에 사용하던 데이터들을 자동으로 복구하고 서비스를 계속 이어서 하게 된다.

이러한 장애를 극복하기 위해서는 JEUS MQ 서버들 간에 네트워크을 구성하고 JEUS MQ 클라이언트에 필요한 설정들을 해야 한다. 그리고 클라이언트들은 장애 극복을 위해서 JEUS MQ에서 제공하는 Client API를 호출하여 장애 극복에 필요한 여러 속성들을 직접 설정할 수 있다.

본 절에서는 JEUS MQ의 장애 극복을 이용하기 위해서 필요한 서버들 간의 네트워크 구성과 기타 설정들에 대해서 설명한다.

JEUS MQ 장애 극복을 위해서는 한 대 이상의 Active 서버가 필요하고, 그 서버들은 클러스터링되어 있어야 한다. Standby 서버는 선택 사항이며, 장애가 발생할 때 전체 처리 가능 용량에 여유를 두기 위해서 설정한다. JEUS 클러스터링 설정은 JEUS Domain 안내서”의 “제5장 JEUS 클러스터링”을 참고한다.

JEUS MQ 클러스터링과 JEUS MQ 장애 극복은 통합된 기능으로 JEUS MQ 클러스터링을 설정하면 JEUS MQ 장애 극복 기능도 함께 동작한다. 이는 예전의 Active 서버와 Standby 서버를 쌍으로 구성해야 했던 점을 개선하여 자유롭고 다양한 구성을 가능하게 하고 Active 서버의 수와 Standby 서버의 수 역시 자유롭게 설정할 수 있다. 일반적으로 다수의 Active 서버에 소수의 Standby 서버를 두는 구성을 하거나 Standby 서버 없이 Active 서버만으로 구성한다.

다음은 장애 극복을 위한 MQ 서버들 간의 네트워크 구성이다.


위와 같은 구성에서 Active 서버들 중 하나에 장애가 발생하면 우선 아무런 서비스를 하고 있지 않은 Standby 서버들 중 하나가 그 역할을 이어받아서 서비스한다. 서비스를 수행하고 있는 Active 서버들과 Standby 서버들 중 하나에 추가적으로 장애가 발생하면 마찬가지로 서비스를 하고 있지 않은 Standby 서버들 중 하나가 이어받게 되며, 더 이상 서비스를 수행하고 있지 않은 서버가 없다면 서비스를 수행하고 있는 Active 서버들 중의 하나가 이어받아서 한 서버가 두 개 이상의 서버 역할을 하게 된다. 이는 해당 구성에서 마지막 하나의 서버만 남을 때까지 계속되며 마지막 남은 서버에도 장애가 발생하면 JEUS MQ 클러스터링 서비스는 더 이상 동작하지 않는다.

Active 서버와 Standby 서버 구성

WebAdmin에서 [Servers] > [서버명] > [Engine] > [Jms Engine] > [Basic] 메뉴를 선택하면, Jms Failover 설정 화면이 나타난다.

다음은 Active 서버와 Standby 서버의 장애 극복 설정을 하는 예제이다.

Persistence Store는 DeliveryMode가 PERSISTENT일 때 메시지를 저장하는 역할을 한다.

장애가 발생했을 때 다른 Active 서버나 Standby 서버가 이 Persistence Store에 저장된 메시지를 복구하기 때문에 메시지의 유실 없이 서비스를 계속할 수 있다. 따라서, JEUS MQ 서버의 장애 극복의 가장 핵심적인 리소스이다.

JEUS MQ 장애 극복에서 Persistence Store를 설정하기 위해서는 가능하면 Persistence Store가 각 Active 서버나 Standby 서버에서 모두 접근 가능한 곳에 위치해야 한다.

참고

만일 일부 서버 간에 Persistence Store에 서로 접근할 수 없는 경우는 최대한 접근 가능한 서버를 찾아서 장애 극복을 시도한다.

서버나 네트워크에 장애가 발생하여 JEUS MQ 클라이언트와 서버 간의 연결이 끊어지면 클라이언트는 클러스터 내의 Active 서버와 Standby 서버들을 교대로 재연결을 시도한다. 재연결이 성공하면 클라이언트는 연결이 끊어지기 이전 상태로 복구를 시도한다. 이런 클라이언트 장애 극복 과정은 클라이언트 애플리케이션에 대한 수정 없이 JEUS MQ 설정을 통해서 자동으로 수행된다.

본 절에서는 클라이언트의 장애 극복에 대해서 자세히 알아보고 그에 따른 제약 사항과 메시지 유실 없이 장애를 극복하기 위한 방법에 대해서 설명한다.

JEUS MQ 클라이언트와 서버의 연결이 끊어질 경우 재연결 여부를 결정하는 'Reconnect Enabled' 항목과 관련 설정 항목들은 해당 Connection Factory를 이용하여 생성하는 모든 커넥션에 적용된다. 이에 대한 자세한 내용은 “5.2.2. Connection Factory 설정”을 참고한다.

만약 특정 커넥션에 대해서 이 설정을 변경하려면 다음의 예제와 같이 JEUS MQ Client API인 "jeus.jms.client.facility.connection.JeusConnection" 클래스를 이용하여 설정을 변경할 수 있다.

. . .
import jeus.jms.client.facility.connection.JeusConnection;
. . .
Context ctx = new InitialContext();
ConnectionFactory factory = ctx.lookup("connection-factory");
JeusConnection connection = (JeusConnection)factory.createConnection("jeus", "jeus");
connection.setReconnectEnabled(true);
connection.setReconnectInterval(1000); // 1초
connection.setReconnectPeriod(3600000); // 1시간
. . .

서버의 Connection Factory 설정을 통해 'Reconnect Enabled'의 값을 true로 설정하였다면 모든 재연결 과정은 클라이언트 애플리케이션에 자동으로 수행되어 클라이언트 코드는 수정할 필요가 없다.

커넥션 복구가 설정되지 않은 JEUS MQ의 커넥션들은 기본적으로 물리적 연결(Socket)을 공유한다. 그러나 domain.xml의 Connection Factory 설정에서 <reconnect-enabled>의 값이 true로 설정된 경우에는 장애 극복을 위해 각각의 커넥션과 물리적 연결이 1대 1 관계가 된다.

참고

물리적 연결과 커넥션이 1대 1 관계가 되면 매번 연결을 맺을 때 마다 새로운 물리적 연결을 생성하게 되므로 성능이 다소 저하될 수 있다. 이 문제는 매번 커넥션을 생성하지 않고 재사용하도록 클라이언트 애플리케이션을 작성함으로써 해결이 가능하다.

연결이 복구될 때 커넥션이 재연결될 뿐만 아니라 커넥션의 상태도 모두 복구된다.

  • 시작(start) 상태

    메시지 수신을 위해서 Connection.start()를 호출한 경우 발생한 장애가 복구되면 시작 상태가 복구되어 메시지 수신을 계속할 수 있다.

  • 정지(stop) 상태

    메시지 수신을 중지하기 위해서 Connection.stop()을 호출한 경우 장애가 복구되면 정지 상태가 복구되어 더 이상 메시지 수신을 하지 않는다.

커넥션 상태뿐만 아니라 커넥션 이하의 세션과 커넥션 메시지 수신자(ConnectionConsumer)들도 모두 복구된다.

이 외에도 세션이나 커넥션 메시지 수신자를 생성하는 메소드들은 장애가 복구되면 요청을 재전송하여 응답이 오기까지 기다리게 된다. Connection.close가 호출되는 경우에는 응답이 오는 것에 상관없이 발생한 장애를 복구하지 않는다.

세션은 Session.close()가 호출되지 않은 경우라면 커넥션이 복구될 때 같이 복구된다. 또한 세션 이하의 메시지 수신자(MessageConsumer)나 메시지 송신자(MessageProducer)들도 세션이 복구될 때 모두 복구된다.

세션에는 여러 가지 객체를 생성하는 메소드들을 가지고 있는데 각 메소드는 발생한 장애를 복구할 때 다음과 같이 동작한다.

세션에 장애가 발생하면 세션 트랜잭션은 다음과 같은 영향을 받는다.

구분동작
commit()

트랜잭션 세션에서 생성한 메시지 수신자와 메시지 송신자를 이용하여 메시지를 수신하거나 전송하는 중에 장애가 발생한 경우 이후 첫 번째로 호출되는 commit은 "javax.jms.TransactionRolledBackException"을 발생하며 rollback된다. commit 시점에 commit할 메시지가 없다면 이 예외는 발생하지 않는다. 장애에 의해 commit이 실패된 후 호출되는 commit은 모두 정상적으로 동작한다.

만약 commit 중에 장애가 발생했는데 RequestBlockingTime이 지난 후에도 장애가 복구되지 않는다면 JMSException이 발생한다. 이 경우 commit 여부는 알 수 없으며 관리 도구를 통해서 commit 여부를 확인해야 한다.

rollback()

rollback은 장애 극복 후에도 rollback 요청을 완료한다.

만약 rollback 중에 장애가 발생했는데 RequestBlockingTime이 지난 후에도 장애가 복구되지 않는다면 JMSException이 발생한다. rollback의 경우 JMSException이 발생해도 rollback되는 것을 보장할 수 있다.

Session.recover() 메소드의 경우 장애가 복구된 후에도 recover 요청을 완료한다. 만약 recover 호출 후에 장애가 발생했는데 RequestBlockingTime이 지난 후에도 장애가 복구되지 않는다면 JMSException이 발생한다.

세션의 ACKNOWLEDGE 모드를 Session.CLIENT_ACKNOWLEDGE로 설정한 경우 Message.acknowledge()를 통해서 세션 내에 존재하는 acknowledge가 안 된 메시지들에 대해서 acknowledge를 호출할 수 있다. 만약 acknowledge 중에 장애가 발생하면 각 메시지에 대해서 ExceptionListener를 통해서 jeus.jms.common.message.MessageAcknowledgeException을 통보받게 된다. 이 예외는 메시지 acknowledge 중에 장애가 발생하여 메시지가 재전송(Redelivered)될 수 있음을 알려준다.

참고

MessageAcknowledgeException.getErrorCode()를 호출하면 실패한 메시지의 MessageID를 얻을 수 있다.

본 절에서는 메시지 전송자를 통해서 메시지를 전송할 때 장애가 발생할 경우에 대해서 설명한다.

메시지 송신자의 send 메소드는 메시지가 서버에 전달되어 응답되기까지 blocking된다. 만약, 도중에 장애가 발생할 경우 다음과 같은 상황이 발생한다.

  • send를 호출했지만 메시지가 아직 전송되지 않은 경우

    이 경우 장애가 복구된 후에 메시지는 서버로 전송되고 정상 처리된다. 만약 RequestBlockingTime이 지난 후에도 장애가 복구되지 않는다면 JMSException이 발생한다.

  • send를 호출하고 메시지가 서버에서 처리된 후에 네트워크에 장애가 발생한 경우

    이 경우 장애가 복구되어 다시 서버에 접속하면 응답 메시지를 받아 정상적으로 처리된다. 만약 RequestBlockingTime이 지난 후에도 장애가 복구되지 않는다면 JMSException이 발생한다.

  • send를 호출하고 메시지가 서버에서 처리된 후에 서버에서 장애가 발생하였고 그 장애가 복구된 경우

    이 경우 장애가 복구되어 다시 서버에 접속해도 메시지 전송 여부를 알 수 없으므로 RequestBlockingTime만큼 기다리다가 ExceptionListener를 통해서 "jeus.jms.common.message.MessageSendException"을 통보한다.

  • send를 호출하고 메시지가 서버에서 처리되기 전에 네트워크나 서버에 장애가 발생한 경우

    이 경우에도 위의 경우와 마찬가지로 복구된 서버에 다시 접속해도 메시지 전송 여부를 알 수 없으므로 RequestBlockingTime만큼 기다리다가 ExceptionListener를 통해서 "jeus.jms.common.message.MessageSendException"을 통보한다.

    참고

    MessageSendException.getErrorCode()를 호출하면 실패한 메시지의 MessageID를 얻을 수 있다.

메시지 수신은 크게 동기와 비동기 방법으로 나눠지며 장애가 복구된 경우 동작 방식이 조금씩 다르다. 우선 동기식 메시지 수신하는 경우 장애 복구에 대해서 설명한다.

동기식 메시지 수신 복구

메시지 수신자에는 MessageConsumer.receive()와 MessageConsumer.receive(long timeout), MessageConsumer.receiveNoWait() 3가지 동기식 메시지 수신 메소드가 있다.

각 메소드 호출할 때 장애가 발생하면 다음과 같이 동작한다.

구분동작
receive()

receive() 메소드의 경우 원래 메시지를 수신할 때까지 blocking되지만, 장애가 발생하면 언제 복구될 지 알 수 없으므로 대기시간을 RequestBlockingTime으로 변경한다. 만약 시간 내에 장애가 복구되면 메시지 수신을 요청하는 메시지를 다시 보내서 메시지를 수신하고 그렇지 않으면 JMSException이 발생한다.

Session.AUTO_ACKNOWLEDGE로 설정한 경우에는 메시지를 수신하고 클라이언트에 메시지를 넘겨주기 전에 acknowledge를 서버에 전송한다. 이때 장애가 발생하면 acknowledge 실패한 메시지에 대해서 ExceptionListener를 통해 jeus.jms.common.message.MessageAcknowledgeException을 통보한다. 이 Exception은 메시지 acknowledge 중에 장애가 발생해서 메시지가 재전송(Redelivered)될 수 있음을 알려준다.

receive(long timeout)

receive(long timeout) 메소드는 원래 메시지를 수신할 때까지 blocking되지만, 장애가 발생하면 언제 복구될지 알 수 없으므로 타임아웃이 RequestBlockingTime보다 큰 경우 제한 시간을 RequestBlockingTime으로 변경한다. 만약, 제한 시한 내에 장애가 복구되면 메시지 수신을 요청하는 메시지를 다시 보내어 메시지를 수신하고 그렇지 않으면 JMSException이 발생한다.

Session.AUTO_ACKNOWLEDGE로 설정한 경우 메시지를 수신하고 클라이언트에 메시지를 넘겨주기 전에 acknowledge를 서버에 전송한다. 이때 장애가 발생하면 acknowledge 실패한 메시지에 대해서 ExceptionListener를 통해서 jeus.jms.common.message.MessageAcknowledgeException을 통보한다. 이 Exception은 메시지 acknowledge 중에 장애가 발생하여 메시지가 재전송(Redelivered)될 수 있음을 알려준다.

receiveNoWait()receiveNoWait() 메소드는 원래 메시지를 수신하지 않아도 blocking되지 않는다. 따라서 장애가 발생해도 바로 반환된다.

비동기식 메시지 수신 복구

비동기식 메시지 수신 복구 방법으로 수신한 메시지들은 MessageListener.onMessage 중이거나 MessageListener.onMessage에서 처리되어 acknoweldge 중이거나 미리 가져오기를 통해 클라이언트 큐에 쌓여 있는 메시지들로 나눌 수 있다.

각각의 메시지들에 대해서 JEUS MQ는 다음과 같이 장애를 복구한다.

  • onMessage 중에 장애가 발생하면 장애가 복구된 후에 acknoweldge가 전송되므로 정상적으로 처리된다.

  • onMessage가 완료된 후 acknoweldge가 전송되는 중에 장애가 발생하면 acknowledge 실패한 메시지에 대해서 ExceptionListener를 통해서 jeus.jms.common.message.MessageAcknowledgeException을 통보한다. 이 Exception은 메시지 acknowledge 중에 장애가 발생하여 메시지가 재전송(Redelivered)될 수 있음을 알려준다.

  • 미리 가져오기로 클라이언트 큐에 쌓여 있는 메시지들은 장애 극복 후에 모두 서버로 되돌려 보내지며 나중에 다시 클라이언트로 전송된다. 이 메시지들의 Message.getJMSRedelivered()의 값은 true가 될 수 있다.

참고

MessageAcknowledgeException.getErrorCode()를 호출하면 실패한 메시지의 MessageID를 얻을 수 있다.