티스토리 뷰

728x90
반응형

 포스팅은 JMS Failover Test에 대해 알아보겠습니다.



1. JMS Fail-Over

- JMS 서버이 고가용성을 위한 것이며, Active - Standby 구성으로 이루어 져있다.

 

 

2. Active - Standby 구조

Active가 비정상적으로 죽었을때만 Standby에서 동작한다. Active는 정상적으로 떠 있고 Standby는 문제가 있을 때 기동된다.
즉 stadnby의 경우는 jms엔진만 떠 있고 factory, destination등을 만들지 않고, 또한 jndi서버에 export-name도 등록하지 않는다.

당연히 port도 listen하지 않는다. Active가 비정상적으로 종료했을때만 gms에서 감지하여, standby를 활성화 시킨다.
(factory, destination, persist store등을 동작하게 하고, jms포트도 listen하게 한다.)

 

여기서 중요한 것을 비정상적인 종료이다. 즉 네트웍 문제나, 엔진이 비정상적으로 죽었을때(ex kill -9)이다.

 

정보: Sending FailureNotificationSignals to registered Actions. Member: nrson_container1
[2013.11.21 23:28:54][4][b288] [container2-148] [JMS-12273] failure recovery event occurred. member token is nrson_container1.


정상적인 다운은 fail-over 조건이 아니다 유의하자.(아래 참고)

 

[2013.11.21 19:51:32][4][b288] [container2-116] [JMS-12253] planned shutdown event occured. member token is ibmtest_container1.


개인적인 의견으로는 운영상에서는 정상다운했을때 fail-over가 되어야 된다고 생각되는데.(session서버도 그렇고…)

 

3. Fail Over 조건

위에서 fail-over조건은 서버가 비정상적인 종료 조건이 되어야 한다고 했다. fail-over를 시작할 때 2가지의 경우가 있다.
처음 standby가 기동될(jboot) 때와, stadnby가 기동된 상태에서, active의 비정상 종료를 감지했을 경우이다.
두 경우 모두 Active와의 연결 재시도를 한 뒤에,(아래 retry)

 

 

[standby JMSMain.xml 설정중]
<fail-over>
<standby>
<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>5</fail-over-max-try-count>
<auto-switchover>true</auto-switchover>
</standby>
</fail-over>

그래도 접속이 되지 않되면, fail-over를 시작한다.

 

[2013.11.21 23:29:19][2][b288] [container2-150] [JMS-12186] starting fail-over for the active broker 'active'.


 

4. NODE클러스터링

 메뉴얼에서는 fail-over가 정상적으로 동작하기 위해서는 노드 클러스터링이 되어야 한다고 하지만,

gms에서 cluster을 관리해 주기 때문에 굳이 노드 클러스터링 될 필요가 없다

하지만 클라이언트가 jndi lookup하기 위해서는 node클러스링 되야지만 remote서버에서 정상적으로 lookup을 할 수 있을 것으로 보인다.

 

그렇지만, 클라이언트가 lookup시 아래와 같이 bacup까지 설정해 주면 굳이 필요 할 것 같지 않다.

 1) 소스에 넣는 방법
  env.put(Context.INITIAL_CONTEXT_FACTORY,"jeus.jndi.JEUSContextFactory");
  env.put(Context.URL_PKG_PREFIXES,"jeus.jndi.jns.url");
  env.put(Context.PROVIDER_URL,"kkam:9736,kkm-PC:9736");

  

 2) JVM property에 추가
-Djava.naming.factory.initial=jeus.jndi.JEUSContextFactory
-Djava.naming.factory.url.pkgs=jeus.jndi.jns.url 
-Djava.naming.provider.url=kkam:9746,kkm-PC:9736

 

3)JEUS에서 제공하는 메소드 사용

JeusConnectionFactoryCreator의 인스턴스를 생성한 후에
.addBrokerAddress(host, port, host, port) 메소드로 active standby 주소를 추가하고
.setReconnectEnable(boolean enabled);
.setReconnectPeriod(long period);
.setReconnectInterval(long interval);

메소드들을 서버의 ConnectionFactory에 설정하듯이 설정한 후에 createConnectionFactory() 메소드로 ConnectionFactory를 생성하면 됩니다

(Enterprise구매하지 않는 standard환경에서 생각해 봄직하다.)

 

* 참고

1NODE 2container환경에서 메세지 보낼때 아래와 같은 로그를 봤을때는

[jeus6@nrson(6008):/home/jeus6/jeus6008/samples/jms/queue]$ jant -f build.xml send

Buildfile: build.xml

 

send:

 

runjavaclient:

     [java] [2013.11.25 00:52:02][2][] [client-1] [JMS-2053] connecting to server at 172.30.1.2:19741(null), 172.30.1.2:39741(null) with Thread[main [client-1],5,main] for 5000ms

=> active, standby로 알아서 연결을 해주는 것으로 보인다.

  ( 2NODE ecah container에서 확인해봐야함)

 

Two-NODE ecah container에서 확인해 보았음 아래와 같이 동일하게  IP포트가 생김

[jeus6@nrson(6008):/home/jeus6/jeus6008/samples/jms/queue]$ jant -f build.xml send

Buildfile: build.xml

 

send:

 

runjavaclient:

     [java] [2013.11.25 01:25:23][2][] [client-1] [JMS-2053] connecting to server at 172.30.1.2:19741(null), 172.30.1.2:39741(null) with Thread[main [client-1],5,main] for 5000ms

 

BUILD SUCCESSFUL

Total time: 3 seconds

fail-over후 호출하면 아래와 같이 오류 발생함..-_-

[java] [2013.11.25 01:34:33][2][] [client-1] [JMS-2053] connecting to server at 172.30.1.2:19741(null), 172.30.1.2:39741(null) with Thread[main [client-1],5,main] for 5000ms
     [java] [2013.11.25 01:34:33][1][] [client-1] [Network-0708] <Connector> Exception occurred during trying to connect to [172.30.1.2:19741(null)]
     [java] <<__Exception__>>
     [java] java.net.ConnectException: Connection refused
     [java]     at sun.nio.ch.Net.connect(Native Method)
     [java]     at sun.nio.ch.SocketChannelImpl.connect(SocketChannelImpl.java:532)
     [java]     at sun.nio.ch.SocketAdaptor.connect(SocketAdaptor.java:81)
     [java]     at jeus.io.impl.nio.ChannelConnector.connect(ChannelConnector.java:30)
     [java]     at jeus.net.impl.Connector.tryToConnect(Connector.java:122)
     [java]     at jeus.net.impl.Connector.getSocketStream(Connector.java:300)
     [java]     at jeus.net.Endpoint.getSocketStream(Endpoint.java:432)
     [java]     at jeus.transport.jeus.JEUSClientTransport.doStart(JEUSClientTransport.java:60)
     [java]     at jeus.server.lifecycle.LifeCycleSupport.start(LifeCycleSupport.java:27)
     [java]     at jeus.jms.client.comm.JEUSConnector.connect(JEUSConnector.java:39)
     [java]     at jeus.jms.client.comm.JEUSFailoverConnector.connect(JEUSFailoverConnector.java:49)
     [java]     at jeus.jms.client.JMSRemoteServerEntry.connect(JMSRemoteServerEntry.java:1216)
     [java]     at jeus.jms.client.JMSRemoteServerEntry.establish(JMSRemoteServerEntry.java:1154)
     [java]     at jeus.jms.client.JMSRemoteServerEntry.start(JMSRemoteServerEntry.java:386)
     [java]     at jeus.jms.client.DedicatedJMSServerEntryFactory.createServerEntry(DedicatedJMSServerEntryFactory.java:37)
     [java]     at jeus.jms.client.SingleJMSServerEntryFactory.createServerEntry(SingleJMSServerEntryFactory.java:95)
     [java]     at jeus.jms.client.JMSServerEntryFactory.createServerEntry(JMSServerEntryFactory.java:48)
     [java]     at jeus.jms.client.facility.factory.JeusConnectionFactory.createJMSServerEntry(JeusConnectionFactory.java:337)
     [java]     at jeus.jms.client.facility.pool.JeusPooledConnectionFactory.createConnection(JeusPooledConnectionFactory.java:170)
     [java]     at jeus.jms.client.facility.pool.JeusPooledConnectionFactory.createConnection(JeusPooledConnectionFactory.java:22)
     [java]     at jeus.jms.client.facility.factory.JeusConnectionFactory.createConnection(JeusConnectionFactory.java:365)
     [java]     at jeus.jms.client.facility.factory.JeusConnectionFactory.createQueueConnection(JeusConnectionFactory.java:342)
     [java]     at jeus.jms.client.facility.factory.JeusXAConnectionFactory.createQueueConnection(JeusXAConnectionFactory.java:130)
     [java]     at jms.queue.QueueSend.init(QueueSend.java:37)
     [java]     at jms.queue.QueueSend.main(QueueSend.java:83)
     [java] <<__!Exception__>>
     [java] [2013.11.25 01:34:33][1][] [client-1] [Lifecycle-1101] : failed to start
     [java] [2013.11.25 01:34:33][0][] [client-1] [JMS-2054] failed to connect to server at 172.30.1.2:19741(null)
     [java] <<__Exception__>>
     [java] jeus.net.ConnectorException: fail to connect to local endpoint 172.30.1.2:19741()
     [java]     at jeus.net.Endpoint.getSocketStream(Endpoint.java:435)
     [java]     at jeus.transport.jeus.JEUSClientTransport.doStart(JEUSClientTransport.java:60)
     [java]     at jeus.server.lifecycle.LifeCycleSupport.start(LifeCycleSupport.java:27)
     [java]     at jeus.jms.client.comm.JEUSConnector.connect(JEUSConnector.java:39)
     [java]     at jeus.jms.client.comm.JEUSFailoverConnector.connect(JEUSFailoverConnector.java:49)
     [java]     at jeus.jms.client.JMSRemoteServerEntry.connect(JMSRemoteServerEntry.java:1216)
     [java]     at jeus.jms.client.JMSRemoteServerEntry.establish(JMSRemoteServerEntry.java:1154)
     [java]     at jeus.jms.client.JMSRemoteServerEntry.start(JMSRemoteServerEntry.java:386)
     [java]     at jeus.jms.client.DedicatedJMSServerEntryFactory.createServerEntry(DedicatedJMSServerEntryFactory.java:37)
     [java]     at jeus.jms.client.SingleJMSServerEntryFactory.createServerEntry(SingleJMSServerEntryFactory.java:95)
     [java]     at jeus.jms.client.JMSServerEntryFactory.createServerEntry(JMSServerEntryFactory.java:48)
     [java]     at jeus.jms.client.facility.factory.JeusConnectionFactory.createJMSServerEntry(JeusConnectionFactory.java:337)
     [java]     at jeus.jms.client.facility.pool.JeusPooledConnectionFactory.createConnection(JeusPooledConnectionFactory.java:170)
     [java]     at jeus.jms.client.facility.pool.JeusPooledConnectionFactory.createConnection(JeusPooledConnectionFactory.java:22)
     [java]     at jeus.jms.client.facility.factory.JeusConnectionFactory.createConnection(JeusConnectionFactory.java:365)
     [java]     at jeus.jms.client.facility.factory.JeusConnectionFactory.createQueueConnection(JeusConnectionFactory.java:342)
     [java]     at jeus.jms.client.facility.factory.JeusXAConnectionFactory.createQueueConnection(JeusXAConnectionFactory.java:130)
     [java]     at jms.queue.QueueSend.init(QueueSend.java:37)
     [java]     at jms.queue.QueueSend.main(QueueSend.java:83)
     [java] Caused by: jeus.net.ConnectorException: fail to connect to 172.30.1.2:19741(), the node is not ready or protocol is different
     [java]     at jeus.net.impl.Connector.tryToConnect(Connector.java:109)
     [java]     at jeus.net.impl.Connector.getSocketStream(Connector.java:300)
     [java]     at jeus.net.Endpoint.getSocketStream(Endpoint.java:432)
     [java]     ... 18 more

==> 연결은 양쪽해 하지만, 우선 active에 접속하는 것으로 보인다.

==> 결론적으로 1NODE 2container에서는 fail-over시 정상적으로 JMS서버를 찾아간다.

==>  2NODE echo 1container에서는 fail-over시 대응대는 컨테이너로 연결을 하지 못한다.

 

※ NODE클러스터링 했을때 vs Source에서 Clustering하여 호출했을때 (ex -Djava.naming.provider.url=kkam:9746,kkm-PC:9736) JEUS Manager가 죽었을때만 다른 쪽으로 넘겨줄 것 같다.

따라서 Manager가 살아있고 컨테이너만 죽은 상태이기 때문에 정상적으로 호출 되지 않는다.

==> 테스트를 해보면 맞는것 같다 manager가 죽어야지, JNDI fail-over가 되는 것으로 보인다.

==> standard버전에서는 JNDI으로 호출을 하면 정상 동작하지 않는다.

   

 ※ NODE클러시터링했을때에는 fail-over가 된 이후에도 정상적으로 fail-over된 컨테이너를 찾아갈 수 있다.

 

위와 같은 문제점이 있다면 addBrokerAddress 메소드를 이용한다.

import javax.jms.*;

import javax.naming.*;

import java.util.Hashtable;

 

public class QSender{

        final static String CFName="QueueConnectionFactory";

        final static String DestName="ExamplesQueue";

 

        public static void main(String[] args){

                QueueConnection con = null;

                try{

                        jeus.jms.client.util.JeusConnectionFactoryCreator confcreator = new jeus.jms.client.util.JeusConnectionFactoryCreator();

                        confcreator.setFactoryName(CFName);

                        confcreator.addBrokerAddress("192.168.0.1",9741,"192.168.0.2",9741);

                        QueueConnectionFactory qcf = (QueueConnectionFactory)confcreator.createConnectionFactory();

 

                        con = qcf.createQueueConnection();

                        QueueSession qSession = con.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);

 

               Queue queue = qSession.createQueue(DestName);

 

                        QueueSender qSender = qSession.createSender(queue);

                        con.start();

 

                        TextMessage msg = qSession.createTextMessage("SUCCESS!");

 

                        qSender.send(msg);

               System.out.println("========================");

                        System.out.println("SUCCESS! Sended Message.");

                        System.out.println("========================");

 

                }catch(Exception e){

                        e.printStackTrace();

                }finally{

                        if(con !=null){

                                try{ con.close(); }catch(Exception ex){}

                        }

                }

        }

}

 

 

5. GMS(Group Management Service)

 

제우스에서는 fail-over구성시 네트웍 구성을 GMS 서비스를 이용하고 있다.

이 서비스를 통해서 서로에 대한 상태를 확인한다.

 

GMS는 클러스터 환경의 인스턴스를 구생해주는 서비스라고 보면 된다.($JEUS_HOME/lib/system/shoal.jar)

(GMS is a core service of the Shoal framework)

 

multicast방식을 사용하기 때문에 네트웍에서 이를 지원해야 한다

D클래스 사용하여 제우스 디폴트는(230.30.1.1)

 

multicast를 사용하지 못하는 경우를 대비해서 mutlcast를 사용하게끔 가상화 할 수 있는 방법이 있다.(TCP를 이용하여 가상으로 multicat처럼 동작)

multicast의 사용이 여의치 않을때 이를 TCP socket으로 대체하여 사용하는 옵션입니다.

-Djeus.jms.cluster.virtual-multicast-list=ip-address:port|container-name

 

이때 옵션에 들어가는 서버 정보는 자신의 것이 아닌 다른 서버의 정보를 넣어주어야 합니다.

  • ip-address:port   ->  이 서버와 active-standby 관계로 맺어질 다른 서버의 IP 주소와 JMS가 기동중인 Container의 포트
  • container-name   ->  이 서버와 active-standby 관계로 맺어질 다른 서버에서 JMS가 기동중인 Container 이름

보통의 경우 container port는 설정을 하지 않고 자동으로 생성된 포트를 사용하도록 되어있는데, 그러면 어떤 포트를 지정해야할 지 알 수 없으므로,

JEUSMain.xml container 설정의 <base-port> 항목에 적당한 container의 포트값을 설정해주도록 합니다.

그리고 container-name은 기본적으로 "server-name" + "_" + "container-name" 의 형식으로 표현되므로 

이 방식에 맞게 설정해주시면 됩니다.

      <engine-container>

         <name>container1</name>

         <base-port>10061</base-port>

         <command-option>-Xms256m -Xmx512m -XX:MaxPermSize=128m -Xss180k -Djava.awt.headless=true 

         -Djeus.webservices.jaxrpc.axis=true

         -Djeus.prepend.classpath=/home/jeus6/jeus6008/lib/application/jmstest.jar

         -Djeus.jms.cluster.virtual-multicast-list=nrson:10071|nrson_container2

         

      <engine-container>

         <name>container2</name>

         <base-port>10071</base-port>

         <command-option>-Xms256m -Xmx512m -XX:MaxPermSize=128m -Xss180k -Djava.awt.headless=true 

         -Djeus.webservices.jaxrpc.axis=true

         -Djeus.prepend.classpath=/home/jeus6/jeus6008/lib/application/jmstest.jar

         -Djeus.jms.cluster.virtual-multicast-list=nrson:10061|nrson_container1     

 

6. Health-check방법 2가지

1)multicat방식(기본)

JEUS에서는  230.30.1.1 를 이용한다 따라서 이용할 수 있는지 확인을 헤야 봐야 한다

ex) 확인테스트

머신 1
tcpdump -ni en0 host 230.30.1.1

머신 2
ping 230.30.1.1

 

*참고 multicast

 

multicast ; 멀티캐스트

 

인터넷의 전송 방식은 전송에 참여하는 송신자와 수신자 관점에서 나누어 유니캐스트브로드캐스트, 멀티캐스트로 구분할 수 있다

유니캐스트 전송 방식은 하나의 송신자가 다른 하나의 수신자로 데이터를 전송하는 방식으로 일반적인 인터넷 응용프로그램이 모두 유니캐스트 방식을 사용하고 있다. 브로드캐스트 전송방식은 하나의 송신자가 같은 서브네트웍 상의 모든 수신자에게 데이터를 전송하는 방식이다. 반면 멀티캐스트 전송방식은 하나 이상의 송신자들이 특정한 하나 이상의 수신자들에게 데이터를 전송하는 방식으로 인터넷 화상 회의 등의 응용에서 사용한다

 

그룹 통신을 위하여 다중 수신자들에게 동일한 데이터를 전송하고자 할 경우 유니캐스트 전송방식을 이용한다면 전송하고자 하는 데이터 패킷을 다수의 수신자에게 각각 여러 번 전송해야 하며, 이러한 동일한 패킷의 중복전송으로 인해 네트웍 효율이 저하된다. 또한 수신자 수가 증가할 경우 이러한 문제점은 더 커지게 된다

반면 멀티캐스트 전송이 지원되면 송신자는 여러 수신자에게 한 번에 메시지가 전송되도록 하여, 데이터의 중복전송으로 인한 네트웍 자원 낭비를 최소화할 수 있게 된다

멀티캐스트 전송이 일반적인 유니캐스트 인터넷 응용 분야와 다른 점은 우선 그 전송 패킷에 있다. 일반적으로 TCP/IP 상의 인터넷 응용 프로그램은 데이터의 송신자가 이를 수신할 수신자의 인터넷 주소를 전송 패킷의 헤더에 표시해 패킷을 전송한다. 그러나 멀티캐스트 전송을 위해서는 헤더에 수신자의 주소 대신 수신자들이 참여하고 있는 그룹 주소를 표시하여 패킷을 전송한다

멀티캐스트 전송을 위한 그룹 주소는 D-class IP 주소 (224.0.0.0∼239.255.255.255)로 전세계 개개의 인터넷 호스트를 나타내는 A, B, C-class IP 주소와는 달리 실제의 호스트를 나타내는 주소가 아니며, 그룹 주소를 갖는 멀티캐스트 패킷을 전송받은 수신자는 자신이 패킷의 그룹에 속해있는 가를 판단해 패킷의 수용여부를 결정하게 된다

그러나 현재 인터넷상의 라우터들이 대부분 유니캐스트만을 지원하기 때문에 멀티캐스트 패킷을 전송하기 위하여서는 멀티캐스트 라우터 사이에 터널링이라는 개념을 사용하여 캡슐화된 패킷을 전송한다. 즉 멀티캐스트 주소를 가진 데이터 패킷 헤더 앞에 멀티캐스트 라우터간에 설정된 터널의 양 끝단의 IP 주소를 덧붙여 라우팅을 함으로써 멀티캐스트를 지원하지 않는 일반 라우터들을 거칠 때 기존의 유니캐스트 패킷과 같은 방법으로 라우팅되어 최종적으로 터널의 종착지로 전송될 수 있게 하는 것이다

 

2)virtual muticast방식 (TCP를 이용하여 가상으로 multicat처럼 동작)

-Djeus.jms.cluster.virtual-multicast-list=<active에 대한 설정>,<standby에 대한 설정>

옵션의 이름은 위와 같고 설정값은 다음의 규칙에 맞게 설정하면 됩니다.

<address>:<port>|<virtual-id>

-Djeus.jms.cluster.virtual-multicast-list=61.77.153.207:10121|node1_container1,61.77.153.207:10131|node1_container2

 

7. persist store

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

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

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

ex) 저널 로그 Persistence Store (파일이용Active 서버와 Standby 서버가 모두 접근 가능한 곳에 위치)

    <persistence-store>

        <journal>

            <base-dir>/home/jeus6/jeusmq</base-dir>

        </journal>

        <!--

        <jdbc>

            <data-source>jdbc/datasource</data-source>

        </jdbc>

        -->

    </persistence-store>  

ex) JDBC Persistence Store (디비 이용)

    <persistence-store>

       <!--

        <journal>

            <base-dir>/home/jeus6/jeusmq</base-dir>

        </journal>

        -->

        <jdbc>

            <data-source>jdbc/datasource</data-source>

        </jdbc>

    </persistence-store>  

* file사용시 active, standby가 동시에 파일을 접근하면 오류가 발생한다.

   따라서 active가 살아있는 상태에서 standby가 접근해서는 안된다

   예를 들면 start-up-max-try-count 0으로 셋팅하면 기동시 fail-over가 바로 됨.

         fail-over-max-try-count -1으로 셋팅하여 fail-over가되지 않게 한다.

 

다음시간에 뵙죠.


728x90
반응형