JBoss EAP 6과 친해지기 15탄 - 메시징 서브시스템
본문 바로가기
IT 이야기/JBoss EAP

JBoss EAP 6과 친해지기 15탄 - 메시징 서브시스템

by 찬찬이 아빠 2020. 12. 18.
반응형
  1. 메시징 개념

대부분 요청을 보내면 응답이 오기까지 기다리는 동기 처리에 익숙하여 요청을 보낸 후 응답을 기다리지 않고 곧바로 다른 일을 처리하는 비동기 처리에 대해 막연히 불안하게 생각할 수 있습니다.

 

비동기 처리에 대해 자주 예로 드는 것이 메일입니다. 일상 생활에서 종이로 보내는 메일이나 전자메일 모두 보내는 즉시 답변 메일이 오는 것이 아니라서 기다리지 않습니다. 반면 전화는 실생활에서 경험할 수 있는 대표적인 동기 처리의 예입니다. 통화 상대가 다른 일을 하고 있었더라도 연결되면 전화 통화를 시작하게 됩니다.

 

엔터프라이즈 시스템에서는 일반적으로 MQ(Message Queue) 또는 MOM(Message Oriented Middleware)이라고 부르는 비동기 메시징 시스템을 사용하여 여러 시스템간에 메시지를 전송하여 처리하는 메시지 교환 시스템을 구현해 왔습니다. JMS는 Java 애플리케이션에서 이런 메시징 시스템을 구현하기 위한 표준이며 Java EE에도 초기부터 포함된 비교적 오랜 시간 동안 발전해 온 표준입니다. JMS를 사용하면 Java EE 환경에서 간단하게 비도기 메시지 처리를 구현할 수 있습니다. 비동기 메시징을 사용하면 시스템 성능 향상, 자원 효율화, 처리 순서 지정 등 여러 가지 장점이 있습니다.

 

시스템 간 메시지 전송을 비동기로 설계하면 하드웨어 리소스 사용률이 줄어들고 IO 작업을 최소화하며 네트워크 대역폭을 더 적게 사용할 수 있습니다.

 

동기 전송을 사용하는 RPC 방법은 요청에 대한 응답을 기다려야 하기 때문에 네트워크 지연 시간이 발생합니다.

 

메시징 시스템은 메시지를 보내는 메시지 제공자와 메시지를 받는 메시지 소비자와 분리하여 처리합니다. 메시지 제공자와 소비자는 완전히 독립적이며 유연하고 느슨한 결합 시스템을 만들 수 있습니다.

 

JMS는 메시지 지향 미들웨어(Message Oriented Middleware : MOM)라는 메시징 시스템에 접근하기 위한 표준 인터페이스입니다. 즉, 메시징 시스템 자체의 구현이 아닌 MOM 서비스를 이용하기 위한 Java 기반의 표준 인터페이스입니다. JMS는 Java EE에 포함되어 있으며 JMS 표준은 벤더 중립적입니다.

 

JMS 제공자는 트랜잭션 시스템을 사용하여 변경 사항을 원자적으로 커밋 또는 롤백합니다. RPC를 기반으로 하는 시스템과 달리 메시징 시스템은 요청과 응답 사이에 비동기 메시지 전달 패턴을 주로 사용합니다.

 

JMS를 이용하여 여러 애플리케이션이 서로 메시지를 전송하여 통신할 수 있습니다. 대부분의 메시징 형태는 피어 투 피어 형이고 JMS 애플리케이션은 대부분 '클라이언트'가 됩니다. JMS는 J2EE의 달느 서비스(JTA/JTS, JNDI, jDBC 등)와 연계하여 Servlet, JSP, EJB 애플리케이션에서 사용할 수 있습니다. 또 메시지 드리븐 빈을 통해 JMS 메시지를 수신하여 동작하는 비동기 EJB를 만들 수 있습니다.

 

JMS의 특징을 정리하면 다음과 같습니다.

  • Java 애플리케이션에서 기존 MOM 시스템과 메시지를 주고 받을 수 있습니다.
  • 메시지를 작성하고, 송수신하기 위한 표준 인터페이스가 제공되어 메시징 애플리케이션 개발이 쉬워집니다.
  • 표준화된 메시징 API를 사용하여 메시징 애플리케이션의 이식성을 높일 수 있습니다.

 

 

  2. JMS 메시징 모델

일반적으로 메시징 시스템은 두 가지 비동기 메시징 패턴을 사용합니다. 하나는 메시지 큐 방식의 PTP(point-to-point) 패턴과 다른 하나는 게시/가입 패턴(Publish/Subscribe) 메시지 방식입니다.

 

PTP는 메시지 큐를 사용하고 있어 하나의 대상9destination)에 대해 하나의 소스에서 메시지가 전송됩니다. Pub/Sub은 하나의 소스에서 여러 클라이언트에 같은 메시지를 동시에 전달합니다. JMS는 이 두 가지 메시지 모델과 애플리케이션 개발에 필요한 클라이언트 인터페이스를 제공합니다.

 

 

 

  3. PTP 패턴

PTP 메시징은 먼저 메시지를 큐에 보냅니다. 메시지는 일반적으로 전송을 보장하기 위해 디스크에 저장됩니다. 다음 메시징 시스템이 메시지를 소비자에게 전달합니다. 소비자는 메시지를 처리하고, 처리가 완료되면 승인 메시지를 보냅니다. 승인 메시지를 받으면 메시징 큐에서 메시지는 삭제되고, 다시 배포할 수 없습니다. 메시징 서버가 소비자로부터 승인을 받기 전에 오류가 발생한 경우에는 복구하여 다시 메시지를 소비자에게 보낼 수 있습니다.

 

P2P 메시징에서 같은 큐를 바라보는 여러 소비자가 있을 수 있지만, 특정 메시지는 하나의 소비자에게서만 처리됩니다. 큐에 메시지를 보낸 제공자는 큐에서 메시지를 받는 소비자와 완전히 분리됩니다. 즉, 보낸 사람과 받는 사람이 서로의 존재를 인지할 필요가 없습니다.

 

기업의 도서 주문 시스템의 주문 큐가 P2P 메시징의 전형적인 예제입니다. 각 주문은 주문 큐에 전송되는 메시지가 됩니다. 주문 큐로 메시지를 보내는 주문 시스템을 생각해 봅니다. 메시지가 큐에 도착하면 일단 저장합니다. 이렇게 하면 오류가 발생했을 때 주문 정보가 유실되지 않습니다. 또한, 주문 큐에 많은 소비자가 있다고 생각합시다. 각 소비자는 주문을 처리하는 컴포넌트로 전달합니다. 다양한 메시지를 각종 주문 프로세서에서 처리할 수 있지만 하나의 주문은 1개의 주문 프로세서에서만 처리됩니다. 이렇게하면 주문이 두 번 처리되지 않습니다.

 

주문처리 프로세서가 메시지를 받으면 도서 주문이 완료되고, 주문된 정보가 물류 시스템으로 전송되어 데이터베이스에 주문 정보가 업데이트 됩니다. 주문 프로세서가 데이터베이스를 업데이트하면 주문이 처리되며 완료됐다는 메시지를 서버에게 알립니다. 창고의 재고관리 시스템에 전소할 수도 있고, 데이터베이스의 업데이트는 단일 트랜잭션으로 완료합니다.

 

P2P 메시징 시스템

 

 

  4. 게시 - 가입 패턴

게시-가입 메시징은 게시자(Publisher)가 어떤 가입자(Subscriber)가 있는지 모르는 상태에서 메시지를 보내게 되고 이렇게 전송된 메시지가 여러 가입자에게 모두 전달되는 구조입니다. 즉, 1:N 형식으로 여러 가입자에게 동시에 메시지를 전달하고 싶을 때 사용합니다.

 

JMS에서는 이런 형식의 메시징을 '토픽(Topic)' 이라고 부릅니다.

 

게시-가입 메시징에서도 영속성을 유지할 수 있습니다. PTP 메시징과 마찬가지로 가입자가 메시지를 완전히 소비할 때까지 메시지의 복사본을 유지하고 있다가 처리가 완료되면 삭제하는 방식입니다.

 

게시-가입 메시징의 대표적인 예제는 뉴스 피드입니다. 전세계에서 발생하는 다양한 뉴스가 작성되면 뉴스 피드에 전송됩니다. 뉴스 기사를 받아보고 싶은 많은 구독자가 세계 곳곳에 걸쳐 있습니다. 뉴스 피드를 관리하는 시스템은 구독자들에게 뉴스 메시지의 복사본을 전달하면, 구독자들은 각자에게 전달된 최신 뉴스를 읽어볼 수 있게 됩니다.

 

게시-구독 메시징 시스템

 

 

  5. HornetQ

JBoss EAP 6의 JMS 구현은 JBoss.org 커뮤니티에서 개발된 HornetQ라는 고성능, 고신뢰성, 유연성을 특징으로 하는 비동기 메시징 시스템이 사용되었습니다.

 

JBoss Messaging 2.0이라는 이름으로 프로젝트를 진행하다 HornetQ로 이름을 변경하여 독립 프로젝트로 진행되었습니다.

 

HornetQ의 내부 통신 모듈은 Netty라는 고성능 네트워크 애플리케이션 프레임워크를 사용하고 있습니다. 또, 메시지를 저장하는데 최적화된 저널이라는 파일기반의 영속화 시스템을 갖추고 있습니다. Linux 환경에서 저널을 더 빠르게 처리할 수 있도록 AIO(Asynchronous I/O) 플로그인도 제공하고 있습니다.

 

HornetQ의 유연한 클러스터링 기능을 제공하여 메시지를 분산하여 처리할 수 있습니다. 또, 고가용성이 요구되는 엔터프라이즈에서 사용하는데 문제가 되지 않도록 클러스터링 구성을 라이브 백업 구성으로 구현할 수 있습니다.

 

 

 

  6. JMS의 이용

Java EE 6에서는 JMS는 Full 프로파일에 포함되어 있고 웹 프로파일에는 포함되어 있지 않습니다. 스탠드얼론 모드에서 파일명을 지정하지 않고, JBoss EAP 6를 시작하면 웹 프로파일을 사용하기 때문에 Messaging 서브시스템을 이용하려면 풀 프로파일의 설정 파일인 standalone-full.xml을 지정하고 시작해야 합니다.

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

 

(1) 대상 설정

대상(destination)은 Queue나 Topic의 이름과 클라이언트에서 룩업할 때 사용하는 JNDI 이름을 지정하여 작성합니다. JNDI 이름은 다음 형식으로 지정합니다.

java:jboss/exported/<JNDI 이름>

 

클라이언트에서는 <JNDI 이름>을 사용하여 대상(destination)을 룩업합니다.

 

다음에서 CLI를 사용하여 Queue와 Topic을 생성하고, 삭제하는 방법을 소개합니다. CLI에서 오퍼레이션을 사용하여 Queue나 Topic의 생성, 삭제도 가능하지만, 더 간단하게 설정할 수 있도록 jms-queue, jms-topic이라는 별도의 명령어를 제공합니다.

 

 

(2) Queue 관리 CLI

Queue나 Topic을 생성하거나, 삭제하는 방법에 대해서 설명합니다.

설정 방법이나 값의 확인 방법도 함께 설명합니다.

명령 설명
큐 생성 jms-queue add --queue-address=<큐 이름> --entries=<JNDI 이름>

[standalone@localhost:9999 /] jms-queue add --queue-address=myQueue --entries=queuq/myQueue

[standalone@localhost:9999 /] /subsystem=messaging/hornetq-server=default/jms-queue=myQueue:read-resource
{
    "outcome" => "success",
   "result" => {
      "durable" => true,
      "entries" -=> ["queue/myQueue"],
      "selector" => undefined
   }
}
큐 조회 jms-queue read-resource --queue-address=<큐 이름>

[standalone@localhost:9999 /] jms-queue read-resource --queue-address=myQueue
durable=true
entries
   queue/myQueue
selector=n/a
큐 삭제 jms-queue remove --queue-address=<큐 이름>

[standalone@localhost:9999 /] jms-queue remove --queue-address=myQueue

 

 

(3) 토픽 관련 CLI

토픽도 큐와 마찬가지로 jms-topic 명령을 사용하여 간단하게 생성, 삭제할 수 있습니다.

명령 설명
토픽 생성 jms-topic add --topic-address=<토픽 이름> --entries=<JNDI 이름>

[standalone@localhost:9999 /] jms-topic add --topic-address=myTopic --entries=topic/myTopic

[standalone@localhost:9999 /] /subsystem=messaging/hornetq-server=default/jms-queue=myQueue:read-resource
{
    "outcome" => "success",
   "result" => {
      "durable" => true,
      "entries" => ["queue/myQueue"],
      "selector" => undefined
   }
}
토픽 조회 jms-topic read-resource --topic-address=<토픽 이름>

[standalone@localhost:9999 /] jms-topic read-resource --topic-address=myTopic
entries
   topic/myTopic
토픽 삭제 jms-topic remove --topic-address=<토픽 이름>

[standalone@localhost:9999 /] jms-topic remove --topic-address=myTopic

 

 

  7. DLQ와 ExpiryQueue

DLQ와 ExpiryQeue라는 특수한 큐가 있습니다. DLQ는 Dead Letter Queue의 약어이름 메시지 전송 시 오류가 발생하여 전달되지 못한 메시지가 저장되는 큐입니다. ExpiryQueue는 메시지에 설정할 메시지 유효기간이 지난 겨울에 해당 메시지가 저장되는 큐입니다. 

 

기본적으로 메시지에 대해 DLQ와 ExpiryQueue로 전송하도록 설정되어 있긴 하지만, 애플리케이션에서 사용하기 위한 큐는 정의되어 있지 않습니다. DLQ와 ExpiryQueue를 이용하려면 먼저 사용하려는 큐를 생성해야 합니다. 기본적으로 DLQ와 ExpiryQueue의 JNDI 이름을 아래와 같이 설정하면 됩니다.

큐 이름 항목 설정값
DLQ jms-queue DLQ
Entries java:jboss/exported/DLQ
ExpiryQueue jms-queue ExpiryQueue
Entitier java:jboss/exported/ExpiryQueue

 

DLQ와 ExpiryQueue를 생성하는 CLI는 다음과 같습니다.

항목 방법
DLQ/ExpiryQueue 생성 [standalone@localhost:9999 / ] jms-queue add =queue-address=DQL --entiries=jboss/exported/DLQ

[standalone@localhost:9999 / ] jms-queue add -queue-address=ExpiryQueue --entries=jboss/exported/ExpiryQueue
DLQ/ExpiryQueue 조회 [standalone@localhost:9999 / ] cd subststem=messging/hornetq-server=default

[standalone@localhost:9999 hornetq-server-default] :read-children-resources(child-type=jms-queue
{
   "outcome" => "success",
   "result" => {
        "DLQ" => {
            "durable" => true,
            "entries" => ["jboss/exported/DLQ"],
            "selector" => undefined
         },
         "ExpiryQueue" => {
            "durable" => true,
            "entries" => ["jboss/exported/ExpiryQueue" ],
        }.
   }
}   

 

메시지 유효기간은 JMS의 API에서 설정할 수 있습니다. 메시지 제공자에서 메시지의 유효기간을 설정할 수 있습니다. 시간 단위는 밀리 초 단위로 설정합니다.

javax.jms.MessageProducer.setTimeToLive(long timeToLive)

 

또, 메시지를 전송할 때 메시지마다 유효기간을 다르게 설정할 수 도 있습니다.

 

java.jms.MessageProducer.send(Destination destination, Message message, int delivery Mode, int priority, long timeToLive)

 

지정한 timeToLive 시간 내에 전달되지 못한 메시지들이 ExpiryQueue에 보관됩니다.

 

 

  8. Linux AIO 사용

HornetQ에서 메시지를 저널에 저장하고 읽을 때 기본적으로 Java의 NIO(Nonblocking I/O)를 사용합니다. 리눅스에서는 더 빠른 I/O 처리를 위해 AIO(Asynchronous I/O) 모듈을 제공합니다. HornetQ에서도 리눅스에서 사용하면 Java NIO 보다 더 빠른 IO 처리를 위해 리눅스의 AIO를 사용하는 모듈을 제공하고 있습니다. AIO를 사용하려면 리눅스 커널 2.6 이상에 libaio 패키지가 설치되어 있어야 합니다. 또 AIO를 사용하려면 ext2, ext3, ext4, jfs, xfs와 같은 파일 시스템이 필요합니다. NFS를 사용하면 속도가 많이 떨어지게 되니, NFS에서는 사용하지 않는 것을 권장합니다.

 

(1) libaio 설치

RHEL에서는 다음 명령으로 libaio를 설치합니다.

$ sudo yum install libaio

 

(2) AIO 모듈 설치

AIO 모듈은 JBoss EAP 6의 네이티브 컴포넌트에 포함되어 있습니다. 네이티브 컴포넌트는 플랫폼별로 배포되기 때문에 설치하고자 하는 플랫폼의 파일을 다운로드 받아 사용합니다.

 

설치 방법은 다음과 같이 다운로드 받은 파일의 압축을 풀어 JBoss가 설치된 modules 디렉터리에 복사하면 됩니다.

$ unzip jboss-eap-native-6.2.0-RHEL6-x86_64.zip
$ cp -R jboss-exp-6.2/modules $JBOSS_HOME/ .

 

(3) 저널타입 변경

리눅스에 libaio 패키지를 설치하고, JBoss EAP 6에 네이티브 컴포넌트를 설치한 후 JBoss EAP 6의 HornetQ에서 AIO를 사용하려면 저널타임(journal-type)을 변경해야 합니다. JBoss EAP 6의 full, full-ha 프로파일에서만 메시징 서브시스템을 사용할 수 있습니다.

 

다음 CLI 명령으로 journal-type을 AIO로 변경할 수 있습니다.

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

 

위와 같이 CLI 명령으로 journal-type을 변경하면 사용하는 설정 파일인 domain.xml이나 standalone-full.xml, standalone-full-ha.xml 파일의 내용이 다음과 같이 변경됩니다.

<hornetq-server>
<persistence-enabled>true</persistence-enabled>
<journal-type>ASYNCiO</journal-type>
<journal-min-files>2</journal-min-files>

 

서버를 재시작하면, server.log에 다음과 같은 메시지가 출력되면 AIO를 사용하도록 변경된 것입니다.

16:45:14,165 INFO	[org.hornetq.core.server] (MSC service thread 1-2) HQ221012:Using AIO Journal

 

 

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

반응형

댓글