본 장에서는 JEUS MQ에서 서버나 네트워크에 장애가 발생해서 더 이상 메시징 서비스를 할 수 없을 때 JMS 클라이언트가 다시 연결을 맺고 클라이언트 상태를 복구하는 방법에 대해서 설명한다.
또한 JMS 클라이언트의 복구를 위해서 필요한 서버 구성과 서버 장애 극복에 대해서 설명한다.
JEUS MQ에서의 장애 극복이란 장애가 발생했을 때 클라이언트 애플리케이션이 자동으로 연결을 다시 맺고 클라이언트의 모든 상태를 장애가 발생하기 이전으로 복구하는 것을 의미한다.
장애가 발생하는 원인은 크게 2가지로 다음과 같다.
네트워크 장애
네트워크 장애는 JEUS MQ서버와 클라이언트 사이의 네트워크에 문제가 발생하여 더 이상 통신을 할 수 없는 상태를 말한다. 이 네트워크 장애는 일시적인 장애일 수도 있고 서버가 다운되거나 네트워크가 완전히 사용 불가능할 경우도 포함된다.
네트워크 장애가 발생하면 JEUS MQ 클라이언트들은 네트워크에 장애가 발생한 서버나 이 서버의 백업 서버에 재연결을 시도하게 되고 재연결이 완료되면 클라이언트의 상태를 자동 복구하여 서비스를 계속하게 된다.
서버 장애
서버 장애는 네트워크 장애를 제외하고 서버에 발생하는 모든 장애를 포함한다. 일반적으로 메시징 데이터를 저장하는 디스크나 데이터베이스 작업을 할 때 오류가 발생하거나 메모리 부족 등이 이에 해당한다.
현재 서비스 중인 서버에 장애가 발생하면 대기 중이던 백업 서버는 이전에 사용한던 데이터들을 자동으로 복구하고 서비스를 계속 이어서 하게 된다.
이러한 장애를 극복하기 위해서는 JEUS MQ 서버들 간에 네트워크을 구성하고 JEUS MQ 클라이언트에 필요한 설정들을 해야 한다. 그리고, 클라리언트들은 장애 극복을 위해서 JEUS MQ에서 제공하는 클라이언트 API를 호출하여 장애 극복에 필요한 여러 속성들을 직접 설정할 수 있다.
본 절에서는 JEUS MQ의 장애 극복을 이용하기 위해서 필요한 서버들 간의 네트워크 구성과 기타 설정들에 대해서 설명한다.
JEUS MQ 장애 극복을 위해서는 한 대의 Active 서버당 한 대의 Standby 서버가 필요하다.
Active 서버
장애가 없을 때 클라이언트의 요청을 받아 처리하는 메인 서버이다.
Standby 서버
Active 서버에 장애가 발생할 때 Active 서버가 하던 서비스들을 이어서 하는 백업 서버이다.
다음은 장애 극복을 위한 MQ 서버 간의 네트워크 구성이다.
JEUS MQ가 장애 극복을 하기 전에 우선 Active 서버와 Standby 서버가 위치하는 JEUS 노드들이 클러스터링 되어 있어야 한다. JEUS 노드 클러스터링 설정은 “JEUS Server 안내서”의 “제4장 JEUS 클러스터링”을 참조한다.
JEUS MQ에서는 장애 극복을 위한 네트워크를 구성할 때 JEUS GMS(Group Management Service) 서비스를 이용한다. 이 서비스를 이용하여 Active 서버와 Standby 서버는 상대방의 서버 인스턴스가 시작되었음을 감지하며 Standby 서버는 Active 서버에 장애가 발생하였을 때 이를 탐지할 수 있다.
다음은 JEUS GMS를 이용하여 Active 서버와 Standby 서버를 구성하는 JMSMain.xml의 예제를 통해서 장애 극복하는 방법을 설명한다. 이렇게 Active 서버와 Standby 서버를 설정하면 두 서버들은 서로의 시작 시점과 장애 탐지등을 할 수 있는 상태가 된다.
우선 Active 서버를 설정하기 위해서는 <fail-over>를 <jms-server>에 위치시킨다. 설정하려는 서버가 Active 서버이므로 <active>를 그 아래 추가하고, JEUS GMS를 이용해 통신을 하도록 <listen-transport-url>에 "gms"값을 넣는다.
[예 3.1] Active 서버의 장애 극복 설정 : <<JMSMain.xml>>
<?xml version="1.0" encoding="UTF-8"?> <jms-server xmlns="http://www.tmaxsoft.com/xml/ns/jeus"> <broker-name>active</broker-name> . . . . . . <fail-over> <active> <listen-transport-url>gms</listen-transport-url> </active> </fail-over> . . . . . . </jms-server>
Standby 서버의 설정은 Active 서버 설정과 유사하다. 다만, Standby 서버의 경우 자신이 백업하는 Active 서버의 Broker 이름을 <active-transport-url>에 "gms://${BROKER_NAME}"의 형식으로 설정해야 한다. 여기서 BROKER_NAME은 Active 서버 정보를 설정한 JMSMain.xml의 <jms-server><broker-name>의 값이다.
[예 3.2] Standby 서버의 장애 극복 설정 : <<JMSMain.xml>>
<?xml version="1.0" encoding="UTF-8"?> <jms-server xmlns="http://www.tmaxsoft.com/xml/ns/jeus"> <broker-name>standby</broker-name> . . . . . . <fail-over> <standby> <active-transport-url>gms://active</active-transport-url> </standby> </fail-over> . . . . . . </jms-server>
본 절에서는 장애가 발생했을 때 클라이언트가 재연결을 맺기 위해서 서비스 채널에 어떤 설정을 하는지에 대해서 설명한다.
서비스 채널은 클라이언트의 연결 요청을 받아들이고 커넥션들을 생성하는 JEUS MQ의 서비스이다. 이 서비스 채널은 커넥션 팩토리를 생성하는 경우 지정되게 되어 있다. 해당 커넥션 팩토리는 새로운 커넥션을 생성할 때 지정된 서비스 채널로 연결을 맺게 된다. 만약, 지정된 서비스 채널에 등록된 서버나 네트워크에 장애가 발생하면 Standby 서버로 재연결을 시도해야하는데 이를 위해서 서비스 채널에 백업 주소를 지정해야 한다.
장애 극복을 위해서 Active 서버와 Standby 서버의 네트워크를 구성하였음에도 불구하고 이렇게 백업 서버의 주소를 일일이 지정하는 것은 한 JEUS MQ 서버에 여러 개의 서비스 채널을 등록할 수 있기 때문이다. 즉, 다음과 같이 한 JEUS MQ 서버에서 서비스 채널과 Connection Factory는 1대 N의 관계를 가지고 있으며 Active 서버와 Standby 서버의 서비스 채널은 1대1의 관계를 가진다.
서버에 따라 다음과 같이 주소를 설정한다.
Active 서버의 JMSMain.xml에서 <jms-server><service-config><backup-service-url>에 Standby 서버의 서비스 채널의 주소를 설정한다.
[예 3.3] Active 서버의 백업 서비스 채널 주소 설정 : <<JMSmain.xml>>
<?xml version="1.0" encoding="UTF-8"?>
<jms-server xmlns="http://www.tmaxsoft.com/xml/ns/jeus">
<broker-name>active</broker-name>
. . .
. . .
<service-config>
<name>default</name>
<server-url>tcp://192.168.1.1:19741</server-url>
<blocking-socket>true</blocking-socket>
<connection-timeout>1000</connection-timeout>
<client-keepalive-timeout>10</client-keepalive-timeout>
<!-- standby의 default 서비스 채널의 주소를 설정한다. ->
<backup-service-url>tcp://192.168.1.2:29741</backup-service-url>
</service-config>
. . .
. . .
</jms-server>
Standby 서버의 JMSmain.xml에서도 Active 서버의 서비스 채널의 주소가 백업 주소로 설정된다.
[예 3.4] Standby 서버의 백업 서비스 채널 주소 설정 : <<JMSmain.xml>>
<?xml version="1.0" encoding="UTF-8"?>
<jms-server xmlns="http://www.tmaxsoft.com/xml/ns/jeus">
<broker-name>standby</broker-name>
. . .
. . .
<service-config>
<name>default</name>
<server-url>tcp://192.168.1.2:29741</server-url>
<blocking-socket>true</blocking-socket>
<connection-timeout>1000</connection-timeout>
<client-keepalive-timeout>10</client-keepalive-timeout>
<!-- active의 default 서비스 채널의 주소를 설정한다. ->
<backup-service-url>tcp://192.168.1.1:19741</backup-service-url>
</service-config>
. . .
. . .
</jms-server>
JEUS MQ 클라이언트는 장애가 발생했을 때 Standby 서버로 재연결을 시도하는데, 이때 재연결에 대한 여러 가지 설정을 할 수 있다.
다음은 Connection Factory 설정의 예로 설정 정보는 <connection-factory> 태그 아래 구성한다.
[예 3.5] Connection Factory 설정 : <<JMSMain.xml>>
<?xml version="1.0" encoding="UTF-8"?>
<jms-server xmlns="http://www.tmaxsoft.com/xml/ns/jeus">
<broker-name>active</broker-name>
. . .
. . .
<service-config>
<name>default</name>
<server-url>tcp://192.168.1.1:9741</server-url>
<blocking-socket>true</blocking-socket>
<connection-timeout>1000</connection-timeout>
<client-keepalive-timeout>10</client-keepalive-timeout>
<backup-service-url>tcp://192.168.1.2:29741</backup-service-url>
</service-config>
. . .
. . .
<connection-factory>
<type>queue</type>
<name>qcf</name>
<service>default</service>
<reconnect-enabled>true</reconnect-enabled>
<reconnect-period>0</reconnect-period>
<reconnect-interval>5000</reconnect-interval>
</connection-factory>
. . .
. . .
</jms-server>
다음은 설정 태그에 대한 설명이다.
항목 | 설명 |
---|---|
<reconnect-enabled> | 장애가 발생했을 때 재연결을 할지 여부를 설정한다.
|
<reconnect-period> | 재연결을 시도할 시간을 설정한다. 기본값으로 설정하면 무한대로 시도한다. (기본값: 0) |
<reconnect-interval> | 재연결을 시도할 때 각 시도 사이의 시간을 설정한다. (기본값: 5000, 단위: ms) |
Persistence Store는 DeliveryMode가 PERSISTENT일 때 메시지를 저장하는 역할을 한다.
장애가 발생했을 때 Standby 서버가 이 Persistence Store에 저장된 메시지를 복구하기 때문에 메시지의 유실 없이 서비스를 계속할 수 있다. 따라서, JEUS MQ 서버의 장애 극복의 가장 핵심적인 리소스이다.
JEUS MQ 장애 극복에서 Persistence Store를 설정하기 위해서는 Persistence Store가 Active 서버나 Standby 서버에서 모두 접근 가능한 곳에 위치해야 한다.
Standby 서버는 Active 서버에 문제가 발생할 때 장애를 복구하고 서비스 지연을 방지하기 위한 서버이다. 서버 관리자는 Active 서버에 발생한 장애가 무엇인지를 파악하고 가능한 빨리 장애 요인을 해결하여 Active 서버를 다시 기동해야 한다.
Active 서버가 다시 기동된 후 현재 Standby 서버를 중지하고 데이터를 마이그레이션하고 접속해 있는 클라이언트들을 Active 서버로 옮겨야 하는데 이런 작업을 복원(Fail-Back)이라 한다. 복원은 자동으로도 할 수 있도록 설정할 수 있고, 관리자가 직접 관리 콘솔을 통해 할 수도 있다.
다음은 자동으로 복원하도록 설정하는 예제이다.
[예 3.6] Standby 서버에 자동 복원 설정 : <<JMSMain.xml>>
<?xml version="1.0" encoding="UTF-8"?>
<jms-server xmlns="http://www.tmaxsoft.com/x
ml/ns/jeus">
<broker-name>standby</broker-name>
. . .
. . .
<fail-over>
<standby>
<active-transport-url>gms://active</active-transport-url>
<auto-switchover>true</auto-switchover>
</standby>
</fail-over>
. . .
. . .
</jms-server>
Standby 서버 설정의 <fail-over>의 <auto-switchover>의
값을 true로 설정하면 된다. 기본값은 false이다. 이 값이 false인 경우 관리자는 관리 콘솔을 통해 직접 Active
서버를 복원해야 한다.
이에 대한 자세한 내용은 “JEUS Reference Book”의 “4.3.3.20. switchover”를 참조한다.
본 절에서는 JEUS MQ 장애 극복 구성을 위한 Active 서버와 Standby 서버의 설정 예제를 보여준다.
[예 3.7] Active 서버 설정 : <<JMSMain.xml>>
<?xml version="1.0"?> <jms-server xmlns="http://www.tmaxsoft.com/xml/ns/jeus"> <!-- JEUS MQ 서버를 구분하는 이름 --> <broker-name>active</broker-name> <service-config> <name>default</name> <server-url>tcp://192.168.1.1:19741</server-url> <blocking-socket>true</blocking-socket> <connection-timeout>1000</connection-timeout> <client-keepalive-timeout>10</client-keepalive-timeout> <!--장애시 재연결을 하기 위한 백업(standby) 주소--> <backup-service-url>tcp://192.168.1.2:29741</backup-service-url> </service-config> <fail-over> <active> <!-- failover 네트워크를 구성하기 위한 active의 주소 --> <listen-transport-url>gms</listen-transport-url> <check-health-response-timeout>5000</check-health-response-timeout> </active> </fail-over> <thread-pool> <min>10</min> <max>20</max> </thread-pool> <connection-factory> <type>queue</type> <name>qcf</name> <!-- reconnect를 위한 속성들 --> <reconnect-enabled>true</reconnect-enabled> <reconnect-period>0</reconnect-period> <reconnect-interval>1000</reconnect-interval> </connection-factory> <persistence-store> <journal> <!-- standby도 접근 가능한 디렉터리를 설정한다. --> <base-dir>/home/shared/jeusmq</base-dir> </journal> <!--jdbc> <data-source>TACDataSource</data-source> </jdbc--> </peristence-store> </jms-server>
[예 3.8] Standby 서버 설정 : << JMSMain.xml>>
<?xml version="1.0"?> <jms-server xmlns="http://www.tmaxsoft.com/xml/ns/jeus"> <!-- JEUS MQ 서버를 구분하는 이름 --> <broker-name>standby</broker-name> <service-config> <name>default</name> <server-url>tcp://192.168.1.2:29741</server-url> <blocking-socket>true</blocking-socket> <connection-timeout>1000</connection-timeout> <client-keepalive-timeout>10</client-keepalive-timeout> <!--장애시 재연결을 하기 위한 백업(active) 주소--> <backup-service-url>tcp://192.168.1.1:19741</backup-service-url> </service-config> <fail-over> <standby> <!-- failover 네트워크를 구성하기 위해 standby가 접속할 active의 주소 --> <active-transport-url>gms://active</active-transport-url> <check-health-response-timeout>5000</check-health-response-timeout> <discovery-response-timeout>5000</discovery-response-timeout> <start-up-max-try-count>-1</start-up-max-try-count> <fail-over-max-try-count>0</fail-over-max-try-count> <auto-switchover>true</auto-switchover> </standby> </fail-over> <thread-pool> <min>10</min> <max>20</max> </thread-pool> <connection-factory> <type>queue</type> <name>qcf</name> <!-- reconnect를 위한 속성들 --> <reconnect-enabled>true</reconnect-enabled> <reconnect-period>0</reconnect-period> <reconnect-interval>1000</reconnect-interval> </connection-factory> <persistence-store> <journal> <!-- active도 접근 가능한 디렉터리를 설정한다. --> <base-dir>/home/shared/jeusmq</base-dir> </journal> <!--jdbc> <data-source>TACDataSource</data-source> </jdbc--> </peristence-store> </jms-server>
서버나 네트워크에 장애가 발생하여 JEUS MQ 클라이언트와 서버 간의 연결이 끊어지면 클라이언트는 Active 서버와 Standby 서버를 번갈아 가며 재연결을 시도한다.
재연결이 성공하면 클라이언트는 연결이 끊어지기 이전 상태로 복구를 시도한다. 이런 클라이언트 장애 극복 과정은 클라이언트 애플리케이션을 수정 없이 JEUS MQ 설정을 통해서 투명하게 자동으로 일어난다.
본 절에서는 클라이언트의 장애 극복에 대해서 자세히 알아보고 그에 따른 제약 사항과 메시지 유실 없이 장애를 극복하기 위한 방법에 대해서 설명한다.
“3.2.3. Connection Factory 설정”에서 JEUS MQ 클라이언트와 서버의 연결이 끊어질 경우 재연결의 여부를 결정하는 <reconnect-enabled>와 그에 관련된 설정에 대해서 설명하였다. 이 설정들은 해당 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의 Active 서버와 Standby 서버는 동일한 이름을 가진 Connection Factory를 가지고 있기 때문에 한 번 JNDI Lookup을 통해서 얻은 Connection Factory는 서버나 네트워크의 장애가 발생한 경우에 다시 Lookup할 필요없이 재사용할 수 있다.
Connection Factory와 마찬가지로 JEUS MQ의 Active 서버와 Standby 서버는 동일한 이름을 가진 Destination을 가지고 있다. 따라서 한 번 JNDI Lookup을 통해서 얻은 Destination은 서버나 네트워크에 장애가 발생했을 때에도 다시 JNDI를 Lookup할 필요없이 재사용할 수 있다.
장애 발생 이전에 Destination에 저장되었던 메시지들은 서버 장애가 발생한 경우 모두 자동 복구되므로 클라이언트는 해당 Destination을 통해서 메시징 작업을 계속 할 수 있다.
JEUS MQ의 클라이언트에서 보내는 모든 요청들은 응답이 오기까지 기다리는 요청 응답시간을 갖는다. 이 시간은 RequestBlockingTime이라 하며 기본값은 200000ms이다. 이 시간은 JEUS MQ 서버의 Connection Factory 설정에서 <request-blocking-time>의 값을 설정하여 변경할 수 있다.
[예 3.9] 응답 대기시간 설정 : <<JMSMain.xml>>
<?xml version="1.0" encoding="UTF-8"?>
<jms-server xmlns="http://www.tmaxsoft.com/xml/ns/jeus">
<broker-name>active</broker-name>
. . .
. . .
<service-config>
<name>default</name>
<server-url>tcp://192.168.1.1:9741</server-url>
<blocking-socket>true</blocking-socket>
<connection-timeout>1000</connection-timeout>
<client-keepalive-timeout>10</client-keepalive-timeout>
<backup-service-url>tcp://192.168.1.2:29741</backup-service-url>
</service-config>
. . .
. . .
<connection-factory>
<type>queue</type>
<name>qcf</name>
<service>default</service>
<!-- 기본값 200000밀리초에서 300000밀리초로 변경한다. -->
<request-blocking-time>300000</request-blocking-time>
<reconnect-enabled>true</reconnect-enabled>
<reconnect-period>0</reconnect-period>
<reconnect-interval>5000</reconnect-interval>
</connection-factory>
. . .
. . .
</jms-server>
서버 설정뿐 아니라 각 커넥션별로 다른 설정을 하는 경우 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.setRequestBlockingTime(300000); // 5분 . . .
RequestBlockingTime은 세션 트랜잭션이나 XA의 경우에 트랜잭션 타임아웃의 기본값으로도 사용된다.
커넥션 복구가 설정이 되지 않은 JEUS MQ의 커넥션들은 기본적으로 물리적 연결(Socket)을 공유한다.
그러나, JMSMain.xml의 Connection Factory 설정에서 <reconnect-enabled>의 값이 true로 설정되는 경우에는 장애 극복을 위해 각각의 커넥션과 물리적 연결이 1:1 관계가 된다.
물리적 연결과 커넥션이 1:1 관계가 되면 매번 연결을 맺을 때 마다 새로운 물리적 연결을 만들게 되므로 성능이 다소 저하될 수 있다. 이 문제는 매번 커넥션을 만들지 않고 재사용하도록 클라이언트 애플리케이션을 작성함으로써 해결이 가능하다.
연결이 복구될 때 커넥션이 재연결될 뿐만 아니라 커넥션의 상태도 모두 복구가 된다.
시작(start) 상태
메시지 수신을 위해서 Connection.start()를 호출한 경우 장애 극복이 되면 시작 상태가 복구되어 메시지 수신을 계속할 수 있다.
정지(stop) 상태
메시지 수신을 중지하기 위해서 Connection.stop()을 호출한 경우 장애가 복구되면 정지 상태가 복구되어 더 이상 메시지 수신을 하지 않는다.
커넥션 상태뿐만 아니라 커넥션 이하 세션과 커넥션 메시지 수신자(ConnectionConsumer)들도 모두 복구된다.
이 외에도 세션이나 커넥션 메시지 수신자를 생성하는 메소드들은 장애 극복이 되면 요청을 재전송하여 응답이 오기까지 기다리게 된다. Connection.close가 호출되는 경우 응답이 오는 것에 상관없이 장애 극복을 하지 않는다.
세션은 Session.close()가 호출되지 않은 경우라면 커넥션이 복구될 때 같이 복구된다. 또한, 세션 이하의 메시지 수신자(MessageConsumer)나 메시지 송신자(MessageProducer)들도 세션이 복구될 때 모두 복구된다.
세션에는 여러 가지 객체를 생성하는 메소드들을 가지고 있는데 각 메소드는 장애 극복할 때 다음과 같이 동작한다.
메시지 수신자를 생성하는 메소드들은 장애 극복 후에도 생성 요청을 완료한다. 만약 RequestBlockingTime이 지난 후에도 장애 극복이 되지 않는다면 JMSException이 발생한다.
createConsumer(Destination destination) createConsumer(Destination destination, java.lang.String messageSelector) createConsumer(Destination destination, java.lang.String messageSelector,boolean NoLocal)
세션에 장애가 발생하면 세션 트랜잭션은 다음과 같은 영향을 받는다.
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 Mode를 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을 통보하게 된다. 이 예외는 메시지 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를 얻을 수 있다.
JEUS MQ의 장애 극복이 클라이언트 애플리케이션에 투명하게 자동으로 처리된다. 하지만 메시지 송수신 과정에서 메시지를 유실할 위험이 있어 ExceptionListener를 통해서 이런 메시지들에 대해서 별도로 처리해 주어야하는 문제점이 있다.
엔터프라이즈 메시징 애플리케이션에서 메시지 유실은 치명적인 문제를 일으킬 수 있다. 메시지 유실을 방지하면서 완벽하게 장애 극복을 할 수 있는 유일한 방법은 트랜잭션을 사용하는 것이다.
따라서 다음에 제시하는 방법을 통해서 애플리케이션을 작성할 것을 적극 권장한다.
J2EE 환경에서는 반드시 트랜잭션 내에서 메시지 송수신한다.
서블릿의 경우 UserTransaction을 JNDI로 부터 lookup하여 UserTransaction 내에서 메시지 송수신을 하도록 한다.
EJB의 경우 EJB 메소드의 트랜잭션 속성(TransactionAttribute)을 "Required"나 "RequireNew"로 설정하여 항상 트랜잭션 내에서 메시지 송수신을 하도록 한다.
일반 Java 클라이언트에서는 세션을 생성할 때 Connection.createSession(true, Session.SESSION_TRANSACTED)를 호출하여 생성하도록 한다. 이렇게 생성한 세션은 commit()이나 rollback()호출을 통해서 트랜잭션 내에서 메시지 송수신을 할 수 있다.