JBoss EAP 6과 친해지기 19탄 - 클러스터링 #2
본문 바로가기
IT 이야기/JBoss EAP

JBoss EAP 6과 친해지기 19탄 - 클러스터링 #2

by 찬찬이 아빠 2020. 12. 24.
반응형
  8. EJB 애플리케이션 클러스터

EJB 애플리케이션에서도 클러스터링에서 필요한 로드 밸런싱, 세션 복제, 장애 복구 기능을 모두 제공합니다. 다음은 EJB 애플리케이션을 클러스터링 구성했을 경우, 클라이언트와의 호출을 나타낸 그림입니다.

JBoss EAP 6의 EJB 클러스터링

 

 

Stateful 세션 빈(SFSB)과 Stateless 세션 빈(SLSB)의 클러스터링의 기능은 아래와 같습니다.

세션 빈 종류 로드 밸런싱 세션 복제 장애 복구
Stateful 세션 빈 O O O
Stateless 세션 빈 O X X

 

Stateless 세션 빈은 상태를 가지지 않기 때문에 복제할 세션이 없습니다. Stateful 세션 빈은 로드 밸런싱 기능과 세션을 복제하여 컬러스터링을 구성합니다.

 

  • 로드 밸런싱

EJB 애플리케이션에 대한 로드 밸런싱은 EJB 클라이언트의 라이브러리에서 제공합니다.

 

  • 세션 복제

'EJB 세션 복제'는 웹 세션 복제와 마찬가지로 SFSB의 상태를 다른 서버에 복사해 놓는 기능입니다. 상태를 복제해 놓았기 때문에, 특정 서버 노드가 정지했을 경우에도 상태를 잃지 않고, SFSB를 계속 처리할 수 있습니다.

 

  • 장애 복구

EJB 애플리케이션의 장애 복구는 EJB 애플리케이션 실행 중 서버에 장애가 발생했을 때, 다른 서버 노드가 처리를 계속하는 기능입니다. 이 기능은 EJB 클라이언트와 세션 복제 기능(SFSB)으로 제공합니다. EJB 클라이언트는 서버 노드의 장애를 감지하면, 접속하고 있는 클러스터 내의 다른 서버 노드에서 수행중이던 처리를 다시 실행합니다.

 

 

(1) EJB 클러스터링 테스트

① EJB 애플리케이션 클러스터 환경 구축

EJB 애플리케이션 클러스터링을 구성하려면 웹 클러스터링과 마찬가지로 ha, full-ha 프로파일을 사용하면 됩니다. 스탠드얼론 서버의 경우엔 standalone-ha.xml, standalone-full-ha.xml 파일을 사용합니다.

 

웹 애플리케이션과 같이 ha 프로파일에 미리 EJB 세션 복제 설정이 되어 있기 때문에, 같은 설정의 JBoss EAP 6 인스턴스를 여러 개 실행하면, 자동으로 감지해 클러스터를 구성합니다. 로드 밸런싱만을 사용하고 싶어도 서버 측에 클러스터를 구성해야합니다. EJB 클라이언트에서 원격의 EJB를 로드 밸런싱할 때, 클러스터에 있는 서버 리스트를 받아서 호출하기 때문입니다.

 

 

② Stateful 세션 빈 개발

Stateful 세션 빈을 구현할 때 세션을 복제하고 싶은 클래스에 @Clustered 어노테이션(org.jboss.ejb3.annotation.Clustered)을 기술합니다.

package com.ejb;

import javax.ejb.Remote;
import javax.ejb.Stateful;
import org.jboss.ejb3.annotation.Clustered;

@Stateful
@clustered
@Remote(IHello.class)
public class Hello implements IHello {
	private int conter = 0;
    
    public String doSomething() {
    	return (Hello EJB3 called : " + (++counter) );
    }
}

 

@Clustered 어노테이션을 사용하려면 메이븐 pom.xml 파일에 jboss-ejb3-ext-api를 추가하여 컴파일해야 합니다.

<dependency>
	<groupId>org.jboss.ejb3</groupId>
    <artifactId>jboss-ejb3-ext-api</artifactId>
    <version2.1.0</version>
    <scope>provided</scope>
</dependency>

 

@Clustered 어노테이션을 기술한 클래스를 JBoss에 배포하면, JBoss는 자동으로 세션 복제 대상 클래스를 찾아 세션을 다른 서버 노드에 복제합니다.

 

다음은 server1을 시작 후 server2를 기동시켰을 경우의 로그입니다.

22:09:27,250 INFO	[stdout] (ServerService Thread Pool -- 61)
... 생략 ...
22:09:27,250 INFO	[stdout] (ServerService Thread Pool -- 61) GMS: address=server1/ejb, cluster=ejb, physical address=192.168.0.28:55200
22:09:27,250 INFO	[stdout] (ServerService Thread Pool -- 61)
... 생략 ...
22:09:29,253 INFO	[org.infinispan.remoting.transport.jgroups.JGroupsTransport] (ServerService Thread Pool -- 61) ISPN000094: Received new cluster view: [server1/ejb|0] [server1/ejb]
22:09:29,331 INFO	[org.infinispan.remoting.transport.jgroups.JGroupsTransport] (ServerService Thread Pool -- 61) ISPN000079: Cache local address is server1/ejb, physical addresses are [192.168.0.28:55200]
... 중략 ...
22:0929,481 INFO	[org.jboss.web] (ServerService Thread Pool -- 61) JBAS018210: Register web context: /helloejb
22:09:29,536 INFO	[org.jboss.as.server] (HttpManagementService-threads - 1) JBAS018559: Deployed "helloejb.war" (runtime-name : "helloejb.war")
20:09:47,856 INFO	[org.jboss.as.clustering] (Incoming-3-,shared=udp) JBAS010225: New cluster view for partition ejb (id: 1, delta: 1, merge: false) : [server1/ejb, server2/ejb]
20:09:47,860 INFO	[org.infinispan.remoting.transport.jgroups.JGroupsTransport] (Incoming-3,shared=udp) IPSN000094: Received new cluster view: [server1/ejb|1] [server1/ejb, server2/ejb]

 

③ 리모트 호출 처리의 구현

InitialContext를 초기화해 lookup한 후, 리모트 EJB를 호출합니다. 호출하는 클라이언트 코드는 다음과 같습니다.

package com.ejb;

import java.util.Properties;
import javax.naming.Context;
import javax.naming.InitialContext;

public class HelloClient {
	public static void main(String[] args) throws Exception {
    	Properties p = new Properties();
        p.put(context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
        InitialContext ctx = new InitialContext(p);
        
        IHello ejb = (IHello) ctx.lookup("ejb:/helloejb/Hello!" + IHello.class.getName() + "?stateful");
        
        for (int i=0; i<10; i++) {
        	System.out.println(ejb.doSomething() );
        }
    }
}

 

InitialContext의 초기화에 사용할 프로퍼티 파일에 클러스터 환경용 설정을 추가해야 합니다. 다음은 jboss-ejb-client.properties의 설정 예입니다.

remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED=false
remote.connections=server1,server2
remote.connection.server1.host=192.168.0.101
remote.connection.server1.port=4447
remote.connection.server1.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS=false
remote.connection.server1.username=admin
remote.connection.server1.password=password

remote.connection.server2.host=192.168.0.102
remote.connection.server2.port=4547
remote.connection.server2.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS=false
remote.connection.server2.username=admin
remote.connection.server2.password=password

 

원격의 서버 리스트를 remote.connections에서 server1, server2로 정의하였고, 해당 서버에 대한 정보를 프로퍼티 파일 안에서 server1, server2에 대한 IP, 포트를 설정하였습니다.

 

 

 

  9. JMS 클러스터링

JBoss EAP 6의 JMS 프로바이더인 HornetQ는 컬러스터링 기능과 고가용성 기능(HA)이 따로 제공되고 있습니다. 각각의 기능을 조합하여, 다양한 클러스터 구성이 가능합니다.

 

(1) HornetQ 클러스터링

HornetQ는 로드 밸런싱 기능과 HornetQ 서버로 클러스터 그룹을 구성하여 클러스터링 기능을 제공합니다.

 

HornetQ의 로드 밸런싱은 서버와 클라이언트에서 로드 밸런싱이 가능합니다.

 

서버의 로드 밸런싱은 메시지 프로듀서에서 메시지 수신 시 클러스터에 포함된 다른 노드에 라운드 로빈으로 전달하여 부하를 분산합니다. 다만, 로드 밸런서가 메시지를 다른 노드에 전달하여 분배하였지만, 노드에 메시지 컨슈머가 없을 때는 메시지를 큐(Queue)에 보관합니다. 이렇게 보관된 메시지를 처리하기 위해 메시지 컨슈머가 있는 다른 클러스터 노드에 미시지를 재분배 할 수도 있습니다.

HornetQ 클러스터링 구성

 

 

클라이언트에서의 로드 밸런싱은 클라이언트의 접속 팩토리(ConnectionFactory)에서 만들어지는 Connection에서 클러스터에 포함된 서버들의 정보를 얻습니다. 클라이언트는 서버 중 대상을 선택하여 메시지를 분산하는 방식입니다. 클라이언트는 서버를 선택하는 로드밸런싱 정책을 바꿀 수 있고, 라운드로빈(기본값)과 랜덤 방식을 사용할 수 있습니다.

 

JMS 기능은 full 프로파일에서 제공합니다. 또 클러스터링 기능은 ha 프로파일에서 제공합니다. 따라서 JMS의 클러스터링 기능을 사용하기 위해서는 full-ha 프로파일을 사용해야 합니다. 스탠드얼론 모드에서는 standalone-full-ha.xml 설정 파일을 사용하여 서버를 시작합니다.

$ ./standalone.sh --server-config=standalone-full-ha.xml

 

HornetQ 클러스터링은 다음 그림과 같습니다.

HornetQ 클러스터링 구성

 

JBoss EAP 6 인스턴스에 JMS 기능을 제공하는 HornetQ가 각각 동작하고 있고, 각각의 HornetQ 서버는 로컬 디스크에 메시지를 저장합니다. 앞서 메시징 서브시스템에서 설명한 것과 같이 리눅스 운영체제를 사용하는 경우는 Journal 파일에 기록하는 성능을 향상하기 위해 AIO를 사용하도록 설정하는 것이 좋습니다.

 

<클러스터링 설정>

full-ha 프로파일에서 클러스터링 기능은 clustered 속성으로 설정되어 있습니다.

설정값은 다음의CLI 명령으로 확인할 수 있습니다.

[standalone@localhost:9999 / ] cd /subsystem=messaging/hornetq-server=default

[standalone@localhost:999 hornetq-server=default] :read-attribute(name=clustered)
{
	"outcome" => "success",
    "result" => true
}

 

클러스터링하지 않으려면 다음 CLI 명령을 실행합니다.

[standalone@localhost:9999 / ] cd /subsystem=messaging/hornetq-server=default
[standalone@localhost:9999 hornetq-server=default] :write-attribute(name=clustered,value=false)
{
	"outcome" => "success",
    "response-headers" => {
    	"operation-requires-reload" => true,
        "process-state" => "reload-required"
    }
}

 

클러스터를 사용하려면 먼저 cluster-password를 설정해야 합니다. 다음 CLI 명령을 사용하여 클러스터 패스워드를 설정합니다. JBoss EAP 6를 설치할 때 사용했던 관리자 패스워드를 입력합니다.

 

① CLI로 HornetQ로 이동

[standalone@localhost:9999 / ] cd /subsystem=messaging/hornetq-server=default

 

② 패스워드 설정

[standalone@localhost:9999 hornetq-server=default] :write-attribute(name=cluster-password,value=password)
{
	"outcome" => "success",
    "response-headers" => {
    	"operation-requires-reload" => true,
        "process-state" => "reload-required"
    }
}

 

③ 리로드

[standalone@localhost:9999 /] /:reload

 

④ 설정값 확인

[standalone@localhost:9999 hornetq-server=default] :read-attribute(name=cluster-password)
{
	"outcome" => "success",
    "result" => "password"
}

 

 

(2) 클러스터링 구성 확인

클러스터링이 정상적으로 구성되어 있는지를 CLI를 이용해 확인할 수 있습니다. 모든 노드를 시작하여 cluster-connection의 topology 파라미터를 참조하여 클러스터링을 구성하고 있는 멤버를 확인할 수 있습니다.

 

  • full-ha 프로파일을 사용하는 2개의 노드(192.168.10.22, 192.168.10.23)
  • 각가의 노드에 1개의 HornetQ 서버
  • cluster-connection은 my-cluster(기본값)

 

① CLI로 HornetQ로 이동

[standalone@localhost:9999 / ] cd /subsystem=messaging/hornetq-server=default

 

② cluster-connection에 이동

[standalone@localhost:9999 hornetq-server=default] cd cluster-connection=my-cluster

 

③ 클러스터 connection의 topology를 확인(:read-attribute)

[standalone@localhost:9999 cluster-connection=my-cluster] :read-attribute(name=topology)
{
	"outcome" => "success",
	"result" => "topology on Topology@1b7b162[owner=ClusterConnectionImple@4593540[nodeUUID=238320de-5aea-11e3-b75b-0b74fc5badf6, connector=TransportConfiguration(name=netty, factory=rog-hornetq-core-remoting-impl-neety-nettyConnectorFactory)?port=5645&host=192.168.0.22, address=jms, server=HornetQServerImpl::serverUUID=238320de-5aea-11e3-b75b-0b74fc5badf6]]:
    	238320de-5aea-11e3-b75b-0b74fc5badf6 => TopologyMember[name = undefined, connector=Pair[a=TransportConfiguration(name=netty, factory=org-hornetq-core-remoting-impl-netty-NettyConnectorFactory) ?port=5645&host=192.168.0.22, b=null]]
        632b962a-5739-11e3-b2f9-73f77b88aeca => TopologyMember[name = undefined, connector=Pair[a=TransportConfiguration(name=netty, factory=org-hornetq-core-remoting-impl-netty-NettyConnectorFactory) ?port=5546&host=192.168.0.23, b=null]]
        nodes=2	members=2"

 

클러스터 연결 정보가 다음 형식으로 출력됩니다.

topology on Topology@1b7b162[owner=ClusterConnectionImple@4593540[nodeUUID=238320de-5aea-11e3-b75b-0b74fc5badf6, connector=TransportConfiguration(name=netty, factory=rog-hornetq-core-remoting-impl-neety-nettyConnectorFactory)?port=5645&host=192.168.0.22, address=jms, server=HornetQServerImpl::serverUUID=238320de-5aea-11e3-b75b-0b74fc5badf6]]:

 

두 번째 라인부터 클러스터에 참가하고 있는 노드의 정보가 출력됩니다.

238320de-5aea-11e3-b75b-0b74fc5badf6 => TopologyMember[name = undefined, connector=Pair[a=TransportConfiguration(name=netty, factory=org-hornetq-core-remoting-impl-netty-NettyConnectorFactory) ?port=5645&host=192.168.0.22, b=null]]
632b962a-5739-11e3-b2f9-73f77b88aeca => TopologyMember[name = undefined, connector=Pair[a=TransportConfiguration(name=netty, factory=org-hornetq-core-remoting-impl-netty-NettyConnectorFactory) ?port=5546&host=192.168.0.23, b=null]]

 

여기서 'a=TransportConfiguration..., b=null' 형식으로 출력되는데 a를 라이브 서버 정보,  b는 백업 서버 정보를 나타냅니다.

 

맨 마지막에 HornetQ 클러스터를 구성하고 있는 멤버 수를 표시합니다. 여기서는 HornetQ 서버 수(nodes)는 2, 클러스터르르 구성하고 있는 멤버 수(members)는 2개 입니다.

nodes=2	members=2"

 

 

(3) 라이브-백업 클러스터 구성

HornetQ는 엔터프라이즈 환경에서는 반드시 필요한 신뢰성을 구현하기 위해서 라이브-백업 구성으로 클러스터링을 구성할 수 있습니다.

 

라이브 서버는 JMS 서버로서의 실제 서비스를 제공하는 서버입니다. 백업 서버는 사용되지 않고 있다가 라이브 서버에 장애가 발생할 때 사용되는 대기 상태의 서버입니다.

 

장애 복구가 발생하면 백업 서버가 액티브 되어, 라이브 서버 대신 JMS 서버의 기능을 제공합니다. 장애 복구가 발생하는 조건은 다음과 같습니다.

  • 액티브한 라이브 서버 크래쉬
  • 셧다운(셧다운을 장애 복구가 발생 시점으로 설정했을 경우)

 

또, 장애 복구가 발생 후, 라이브 서버를 재기동하면, 다시 원래 라이브 서버가 서비스하고 백업 서버는 대기 상태가 되는 Fail-back 기능도 제공합니다.

 

HornetQ에서는 라이브-백업 구성 방식으로 데이터 복제와 공유 스터어를 사용하는 2가지 방식의 모드를 제공합니다.

데이터 복제 모드

 

 

데이터 복제 ㅁㅗ드에서는 네트워크를 통해 라이브 서버의 데이터를 백업 서버로 복제합니다. 백업 서버를 시작할 때 라이브 서버의 몯느 데이터를 동기화해야 합니다. 동기화 중에 라이브 서버가 장애가 발생하면, 백업 서버는 라이브 서버를 대체할 수 없게 됩니다.

 

이런 데이터 복제가 필요 없는 방식이 공유 스토어 모드입니다. 라이브 서버와 백업 서버가 같은 저장소(저널 파일 시스템)을 공유하는 방식입니다. 장애 시 백업 서버는 라이브 서버 대신에 JMS 서버 기능을 제공하기 위해서 공유 파일 시스템상에 저널을 사용합니다. 이 방식에서는 서로 다른 서버 간에 파일 시스템을 공유할 수 있는 SAN(Storage Area Network)과 같은 공유 디바이스가 필요합니다.

공유 스토어 모드

 

 

NFS(Network File System) 등의 NAS(Network Attached Storage)는 데이터 전송 성능 문제가 있을 수 있어 추천하지 않습니다. 또, 리눅스의 AIO를 사용할 경우, NFS에서 공유 스토어 모드는 정상적으로 동ㅈ가하지 않습니다.

 

또, HornetQ는 클라이언트 코드에서 자동 장애 복구 방법도 제공합니다. 클라이언트 자동 장애 복구는 라이브 서버에서 장애가 발생해 액티브하게 된 백업 서버로 자동으로 재접속하여 실행 중이던 세션과 메시지 컨슈머를 자동으로 재작성해 처리하는 기능입니다. 애플리케이션에서 장애 시 재접속 방법을 직접 코딩할 필요가 없습니다.

 

 

 

 

  10. 클러스터 그룹

JBoss EAP 6에서는 하나의 물리 네트워크 내에 여러 클러스터 그룹을 구성할 수 있습니다. 지금까지 설명은 기본적으로 네트워크 내에 있는 전체 서버 노드를 하나의 클러스터로 구성하는 것을 전제로 설명했습니다.

 

클러스터 그룹이란 같은 서비스를 제공하는 서버 노드들을 묶어서 네트워크 내에 여러 클러스터를 구축하는 방법입니다. 웹 애플리케이션, EJB 애플리케이션, JMS 각각의 클러스터링에 대해 설명했지만, 클러스터 그룹에 대해서도 각각 애플리케이션 단위 또는 서버 노드 단위로 구성할 수 있습니다.

 

웹, EJB 애플리케이션의 클러스터링을 위해서는  Infinispan과 JGroups 컴포넌트를 이용합니다. 한편, JMS에서는 HornetQ 자체가 클러스터링 기능을 가지고 있습니다.

 

(1) 웹, EJB 컨테이너 클러스터 그룹

웹, EJB 컨테이너는 클러스터링을 설정하는 서버 노드 간의 임의의 서버 노드에 대해서만 그룹을 구성할 수 있습니다. 다음 그림을 보면, 클러스터 그룹 A와 B를 구성하고 있습니다.

웹/EJB 컨테이너의 클러스터 그룹

 

 

<웹, EJB 클러스터 그룹 분할 방법>

웹, EJB 애플리케이션은 클러스터링을 설정하기 위한 도구로 Infinispan과 JGroups라는 컴포넌트를 사용하고 있습니다. 클러스터 그룹을 분할하려면 Infinispan 또는 JGroups의 설정을 변경해야 합니다.

 

클러스터 노드 간 통신에 사용하는 JGroups는 데이터 전송 프로토콜은 UDP 멀티캐스트나 TCP를 사용할 수 있습니다. 기본적으로 UDP 멀티캐스트를 사용하기 때문에 네트워크를 나누려면 멀티캐스트 주소를 변경해야 합니다.

 

JBoss EAP 6의 시스템 프로퍼티 jboss.default.multicast.address를 변경하여 논리적으로 네트워크를 분리할 수 있습니다. 멀티캐스트 주소 기본값은 230.0.0.4입니다.

 

① jboss.default.multicast.address 확인

[standalone@localhost:9999 / ] cd /socket-bomdomg-group=standard-sockets/socket-binding=jgroups-udp

[standalone@localhost:9999 socket-binding=jgroups-udp] :read-resource
{
	"outcome" => "success",
    "result" => {
    	"client-mappings" => undefinded,
        "fixed-port" => false,
        "interface" => undefined,
        "multicast-address" => expression "${jboss.default.multicast.address:230.0.0.4}",
        "multicast-port" => 45688,
        "name" => "jgroups-udp",
        "port" => 55200
        "operation-requires-reload" => true,
        "process-state" => "reload-required"
    }
}

 

② jboss.default.multicast.address를 시작 옵션으로 변경

-u 옵션이나 시스템 프로퍼티를 사용하여 변경할 수 있습니다.

$ ./standalone.sh -u=230.10.0.5 -c=standalone-ha.xml

$ ./standalone.sh -Djboss.default.multicast.address=230.10.0.5 -c=standalone-ha.xml

 

 

(2) HornetQ 클러스터 그룹

HornetQ에서는 클러스터링을 구성하는 노드 그룹을 구성하여 여러 클러스터 그룹을 구성할 수 있습니다. 클러스터링을 구성하는 목직지(Queue/Topic)도 주소 단위로 지정할 수  있고, 주소마다 다른 클러스터링 그룹을 지정할 수도 있습니다. 하나의 JMS 서버가 여러 클러스터 그룹에 동시에 참가할 수 있어 다양한 클러스터 토폴로지를 구성하는 것이 가능합니다.

 

HornetQ는 클러스터 그룹 구성을 쉽게 할 수 있도록 멀티캐스트를 사용하고 있습니다.

 

JBoss EAP 6의 JMS 기능을 제공하는 full 프로파일 중에서 JMS 클러스터링을 사용하려면 full-ha 프로파일을 사용해야 합니다. 스탠드얼론 모드에서는 standalone-full-ha.xml을 사용합니다. full-ha 프로파일에 클러스터 그룹을 구성하기 위한 설정이 이미 정의되어 있습니다. 소켓 바인딩에 설정된 JMS가 사용하는 UDP 멀티캐스트 주소와 포트를 사용하여 클러스터 그룹을 구성합니다.

 

JMS에서 사용하는 소켓 바인딩 설정은 다음과 같습니다.

항목 설명 기본 설정값
multicast-address 소켓이 멀티캐스트의 트래픽을 송수신할 때 사용하는 멀티캐스트 주소 ${jboss.messaging.group.address:231.7.7.7}
multicast-port 소켓이 멀티캐스트 트래픽을 송수신할 때 사용하는 포트 ${jboss.messaging.group.port:9876}

 

JBoss EAP 6를 시작하면 동일 네트워크 내에 있는 HornetQ 서버는 소켓 바인딩의 UDP 멀티캐스트 주소와 포트를 사용하여 자동으로 클러스터 그룹을 구성합니다. 기본 설정으로도 클러스터링 기능을 사용할 수 있도록 충분히 설정되어 있습니다.

 

  • 클러스터 그룹 분할

HornetQ 클러스터 그룹 분할 방법도 웹 컨테이너와 마찬가지로 논리적으로 사용하는 네트워크를 나누어 클러스터 그룹마다 멀티 캐스트 주소와 포트를 변경하는 방법입니다. JBoss EAP 6 시작 인수로 지정하여 간단하게 변경할 수 있습니다.

 

서버 시작 시 시스템 파라미터 jboss.messaging.group.address와 jboss.messaging.group.port를 사용하여 멀티캐스트 그룹을 분리합니다. 보통 멀티캐스트 주소만 변경하여 관리합니다.

$ ./standalone.sh -c=standalone-full-ha.xml -Djboss.messaging.group.address=231.7.7.8 -Djboss.messaging.group.port=9877

 

 

 

  11. Infinispan

JBoss EAP 6에서는 웹 애플리케이션, EJB 애플리케이션의 클러스터링 기능을 제공하기 위해 먼저 소개한 JGroups와 Infinispan이라는 프로젝트를 내부에서 사용하고 있습니다.

 

Infinispan은 JBoss Cache 후속 프로젝트로 개발이 진행되고 있고 'JBoss Data Grid Platform'이라는 별도의 제품으로 판매되고 있습니다. JBoss EAP 6에도 클러스터링을 구현하기 위해서 Infinispan을 사용하고 있지만, 애플리케이션에서 Infinispan의 API를 직접 이용하는 경우에 대해서는 기술지원을 하지 않습니다. 클러스터링 기능을 변경하려면 Infinispan의 캐시 컨테이너 설정을 변경해야 합니다.

 

JBoss EAP 6의 웹 애플리케이션 및 EJB 애플리케이션에서 캐시 데이터 복제 모드는 전체 노드에 모두 복사하는 relplication 방식과 일부 노드에만 복사하는 dist 방식을 제공합니다. 또, 통신 모드는 동기(SYNC) 방식과 비동기(ASYNC) 두 가지 방식을 제공하고 있습니다. 기본값은 replication 방식, 비동기 모드를 사용합니다.

 

시스템 요건에 따라 다르겠지만, 대부분의 경우 위의 기본 설정으로도 충분한 성능과 신뢰성을 기대할 수 있습니다. 그러나 캐시 데이터 복제 모드에 대해서는 클러스터에 참여하는 노드 개수가 많을 경우 복제되는 객체의 개수 및 횟수가 많아져 메모리 이슈가 발생할 가능성이 있기 때문에 dist 모드로 변경하는 것이 좋습니다.

 

 

(1) 캐시 복제 모드

캐시 모드는 복제 대상을 클러스터 전체로 할 것인지, 임의의 노드로 할지를 설정하는 것입니다. 전체를 대상으로 하려면 replication 모드를 선택하고, 임의의 노드로 복제하려면 distribution 모드를 선택합니다. JBBoss EAP 6는 기본값으로 replication 모드로 설정되어 있습니다.

 

① replication 모드

클러스터 그룹 내의 모든 서버 노드에 캐시를 복제합니다. 모든 노드에 복제하기 때문에 어느 서버 노드에 접속하더라도 서버의 로컬에서 캐시 데이터를 사용할 수 있습니다.

 

그러나 모든 서버 노드에 캐시 데이터를 복제하기 때문에 네트워크 트래픽이 많고, 모든 노드의 복제본을 가지기 때문에 메모리 사용량도 많습니다. 또 SYNC 통신을 사용할 경우, 모든 서버 노드에 대해서 캐시 데이터 복제가 완료되어야 다음 작업을 처리할 수 있기 때문에, 서버 노드 수가 많아지면 캐시의 기록 속도가 느려져, 성능에 영향을 미치게 됩니다.

replication 모드의 복제 방법

 

 

② distribution 모드

replicaation 모드는 전체 서버 노드를 복제 대상으로 하고 있지만, distribution 모드는 캐시의 복제 대상을 클러스터 내의 일부 노드들을 대상으로 합니다. 또 복제 대상 서버 노드 개수를 임의로 설정할 수 있습니다. 이를 캐시 오너 수(Cache Owner)라고 합니다.

 

JBoss EAP 5까지는 Buddy Replication이라고 하는 캐시 replication 모드가 있었습니다. 이것은 특정 서버가 클러스터 내의 선택한 특정 서버 노드(buddy)에만 복제하는 기능입니다. Infinispan의 Distribution 모드는 Buddy Replication과 비슷해 보이지만, Distribution 모드는 복제 대상을 지정하지 않아도 내부 알고리즘으로 판단하여 각의 캐시마다 분산하여 복제하는 점이 다릅니다.

 

Distribution 모드에서 캐시는 Consistent Hash 알고리즘을 사용하여 관리합니다. 캐시 복제 대상 개수를 지정하여 사용하기 때문에, 동기(SYNC) 통신을 사용하더라도 캐시의 저장 속도에 영향을 덜 받게 됩니다.

dist 모드의 복제 방법

 

 

③ Consistent Hash

분산 캐시에 사용하는 해시 알고리즘입니다. 단순한 해시에 키를 추가, 삭제했을 경우, 테이블의 사이즈 변경이 필요해서 키를 재 맵핑하는 시간이 오래 걸리게 됩니다.

 

Consistent Hash를 이용하면 이 시간을 최소한으로 줄일 수 있습니다. Infinispan에서도 dist 모드에서 어떤 노드에 데이터를 복제할지 결정하는데 이 알고리즘을 사용합니다. 동적으로 노드의 개수가 증가하고 줄어드는 환경에서 자동으로 데이터를 분산하기 위해 사용하는 알고리즘이 Consistent Hash입니다.

 

데이터를 저장하기 위한 대상 노드를 결정할 때 Consistent Hash 알고리즘을 이용하여 동적으로 노드 수가 증가하거나 감소할 때 자동으로 데이터를 분산합니다. Memcached, Amazon's Dynamo, Cassandra 그리고 Riak 등의 제품에서 Consistent Hashing을 활용하여 파티셔닝을 구현했습니다.

Consistent Hash

 

 

 

(2) 캐시 모드 변경 방법

캐시 모드를 Distribution 모드로 변경하려면 아래와 같이 CLI 명령을 수행합니다. distribution 모드를 사용하는 경우 캐시 오너 수도 설정할 수 있습니다.

 

① web 캐시 컨테이너로 이동

[standalone@localhost:9999 / ] cd /subsystem=infinispan/cache-container=web

 

② 캐시 모드를 distribution 모드로 변경

[standalone@localhost:9999 cache-container=web] :write-attribute(name=default-cache, value=dist)
{
	"outcome" => "success",
    "response-headers" => {
    	"operation-requires-reload" => true,
        "process-state" => "reload-required"
    }
}

 

③ 캐시 오너 수 변경

[standalone@localhost:9999 cache-container=web] cd distributed-cache=dist
[standalone@localhost:9999 distributed-cache=dist] :write-attribute(name=owners, value=3)
{
	"outcome" => "success",
    "response-headers" => {
    	"opeeration-requires-reload" => true,
        "process-state" => "reload-required"
    }
}

 

EJB 애플리케이션의 경우에도 캐시 모드를 변경하려면 위의 CLI에서 'cache-container=ejb'로 변경하여 설정하면 됩니다.

 

 

(3) 통신 방식

통신 방식은 비동기(ASYNC) 모드와 동기(SYNC) 모드의 두 가지 종류가 있습니다. 각각의 특징은 다음과 같습니다.

 

<비동기(ASYNC) 모드>

  • 세션 복제 시 비동기로 처리하여 응답 수신을 기다리지 않고 반환
  • 속도가 빠름

 

<동기(SYNC 모드)>

  • 세션 복지 시 모든 응답이 완료되고 나서 응답
  • 백업까지 모두 성공적으로 된 것을 확인하기 때문에 신뢰성이 높음
  • 데이터 반영 시 락을 걸고, 분산 2단계 커밋을 해서 동일 데이터를 동시에 변경하면 에러가 발생
  • 속도가 상대적으로 느림

 

동기(SYNC) 모드로 변경하려면 CLI 명령으로 아래와 같이 설정을 변경할 수 있습니다.

[standalone@localhost:9999 /] cd /subsystem=infinispan/cache-container=web/replicated-cache=repl
[standalone@localhost:9999 replicated-cache=repl] :write-attribute(name=mode, value=SYNC)
{
	"outcome" => "success",
    "reponse-headers" => {
    	"operation-requires-reload" => true,
        "process-state" => "reload-required"
    }
}

 

 

 

참고서적 : 거침없이 배우는 JBoss

반응형

댓글