본문 바로가기

서버운영 (TA, ADMIN)/네트워크

[오픈소스] 메시지큐(Message Queue) 알아보기

메시지 큐 개념을 다시 잡아보고자 학습용도로 작성한 포스팅입니다. 출처는 http://zzong.net/post/3 입니다.


메시지 지향 미들웨어(Meesage Oriented Middleware: MOM)은 비동기 메시지를 사용하는 다른 응용 프로그램 사이에서 데이터 송수신을 의미합니다. MOM을 구현한 시스템을 메시지 큐(MessageQueue: MQ)라 합니다. 메시지 큐는 별도의 공정 작업을 연기할 수 있는 유연성을 제공하여 SOA(Service-Oriendted Architecture)의 개발에 도움을 줄 수 있습니다.


프로그래밍에서 MQ는 프로세스 또는 프로그램 인스턴스가 데이터를 서로 교환할때 사용하는 방법입니다. 이때 데이터를 교환할 때 시스템이 관리하는 메시지 큐를 이용하는 것이 특징입니다. 이렇게 서로 다른 프로세스나 프로그램 사이에 메시지를 교환할 때 AMQP(Advanced Message Queuing Protocol)을 이용합니다. AMQP는 메시지 지향 미들웨어를 위한 open standard application layer protocol입니다. AMQP를 이용하면 다른 벤더 사이에 메세지를 전송하는 것이 가능한데 JMS(Java Message Service)가 API를 제공하는 것과 달리 AMQP는 wire-protocol을 제공하는데 이는 octet stream을 이용해서 다른 네트워크 사이에 데이터를 전송할 수 있는 포맷으로 이를 사용하게 됩니다.



메시지 큐의 장점

 - 비동기(Asynchronous): Queue에 넣기 때문에 나중에 처리할 수 있습니다.

 - 비동조(Decoupling): 애츨리케이션과 분리할 수 있습니다.

 - 탄력성(Resilience): 일부가 실패 시 전체에 영향을 받지 않습니다.

 - 과잉(Redundancy): 실패할 경우 재실행 가능합니다.

 - 보증(Guarantees): 작업이 처리된걸 확인할 수 있습니다.

 - 확장성(Scalable): 다수의 프로세스들이 큐에 메시지를 보낼 수 있습니다.


Message Queueing은 대용량 데이터를 처리하기 위한 배치 작업이나, 채팅 서비스, 비동기 데이터를 처리할 때 사용합니다. 프로세스 단위로 처리하는 웹 요청이나 일반적인 프로그램을 만들어서 사용하는데 사용자가 많아지거나 데이터가 많아지면 요청에 대한 응답을 기다리는 수가 증가하다가 나중에는 대기 시간이 지연되어서 서비스가 정상적으로 되지 못하는 상황이 오기 때문에 기존에 분산되어 있던 데이터 처리를 한 곳에 집중하면서 메세지 브로커를 두어서 필요한 프로그램에 작업을 분산시키는 방법을 하는 것이 그 목적입니다.



메시지 큐 사용처

 - 다른 곳의 API로 부터 데이터 송수신이 가능합니다.

 - 다양한 애플리케이션에서 비동기 통신을 할 수 있습니다.

 - 이메일 발송 및 문서 업로드가 가능합니다.

 - 많은 양의 프로세스들을 처리할 수 있습니다.



JMS vs AMQP

 - AMQP는 ISO 응용 계층의 MOM 표준입니다.

 - JMS는 MOM을 자바에서 지원하는 표준 API입니다. (JMS와 AMQP는 다른 개념입니다)

 - JMS는 다른 자바 애플리케이션들끼리 통신이 가능하지만 다른 MOM의 통신은 불가능 합니다. (AMQP, SMTP 등)

 - ActiveMQ의 JMS라이브러리를 사용한 자바 애플리케이션들끼리 통신이 가능하긴 합니다. 하지만 다른 자바 애플리케이션(ActiveMQ를 사용안함)의 JMS와는 통신할 수 없습니다.

 - AMQP는 프로토콜만 맞다면 다른 AMQP를 사용한 애플리케이션끼리 통신이 가능합니다. SMTP와도 통신이 가능합니다.

 - JMS 라이브러리는 AMQP를 지원하지는 않습니다.



오픈 소스 메시지 큐


RabbitMQ, ActiveMQ, ZeroMQ, Kafka에 대해 알아보겠습니다.



RabbitMQ

 - AMQT 프로토콜을 구현해 놓은 프로그램입니다.

 - 신뢰성, 안정성과 성능을 충족할 수 있도록 다양한 기능을 제공합니다.

 - 유연한 라우팅: Message Queue가 도착하기 전에 라우팅 되며 플러그인을 통해 더 복잡한 라우팅도 가능합니다.

 - 클러스터링: 로컬네트워크에 있는 여러 RabbitMQ 서버를 논리적으로 클러스터링할 수 있고 논리적인 브로커도 가능합니다.

 - 관리 UI가 있어 편하게 관리 가능합니다.

 - 거의 모든 언어와 운영체제를 지원합니다.

 - 오픈소스로 상업적 지원이 가능합니다.


ActiveMQ

아파치 ActiveMQ는 풀 자바 메시지 서비스(JMS) 클라이언트와 함께 자바로 만든 오픈소스 메시지 브로커입니다. 이 시스템은 "엔터프라이즈 기능" - 이 경우는 하나 이상의 클라이언트와 서버간의 커뮤니케이션을 증진시키는 기능 - 을 제공합니다. JMS 1.1을 통해 자바 뿐만 아니라 다른 "교차언어"를 사용하는 클라이언트를 지원합니다. 커뮤니케이션은 컴퓨터 클러스터링 및 가상메모리, 캐쉬 그리고 저널 지속성을 제외한 어떤 데이터베이스를 JMS 지속성 제공자로 이용할 수 있는지 등의 특징들을 통해 운영됩니다.



주요 특성들

 - 다양한 언어 환경의 클라이언트들과 프로토콜을 지원하여, Java, C, C++, C#, Ruby, Perl, Python, 그리고 PHP 클라이언트들을 지원합니다.

 - OpenWire를 통해 고성능의 Java, C, C++, C# 클라이언트 지원합니다.

 - Stomp를 통해 C, Ruby, Perl, Python, PHP 클라이언트가 다른 인기있는 메시지 브로커들과 마찬가지로 ActiveMQ에 접근할 수 있습니다.

 - Message Groups, Virtual Destinations, Wildcards와 Composite Destinations를 지원합니다.

 - JMS 1.1과 J2EE 1.4를 완벽하게 지원하며, transient, persistent, transactional, 그리고 XA 메시징을 지원합니다.

 - Spring 지원으로 ActiveMQ는 Spring 애플리케이션에 매우 쉽게 임베딩될 수 있으며, Spring의 XML 설정 메커니즘에 의해 쉽게 설정됩니다.

 - Geronimo, JBoss 4, GlassFish, 그리고 WebLogic과 같은 인기있는 J2EE 서버들과 함께 테스트됩니다.

 - Inbound와 Outbound 메시징을 위한 JCA 1.5 Resource Adapter를 포함하여 ActiveMQ가 J2EE 1.4 호환 서버에 자동 배치됩니다.

 - In-VM, TCP, SSL, NIO, UDP, Multicast, JGroups, 그리고 JXTA Transport들과 같은 플러그인 가능한 전송 프로토콜들을 지원합니다.

 - 고성능의 저널을 사용할 때에 JDBC를 사용하여 매우 빠른 Persistnece를 지원합니다.

 - 고성능의 클러스터링, 클라이언트-서버, Peer 기반 통신을 지원을 위한 설계가 되어 있습니다.

 - REST API를 통해 웹기반 메시징 API를 지원합니다.

 - 웹브라우저가 메시징 도구가 될 수 있도록, Ajax를 통해 순수한 DHTML을 사용한 웹스트리밍 지원을 합니다.

 - In-memory JMS Provider로 사용될 수 있으며, 이는 JMS를 사용한 단위 테스트에 적합한 솔루션을 제공합니다.

 - STOMP, AMQP, MQTT, Openwire, SSL, and WebSockets



ZeroMQ

ZeroMQ는 메시징 라이브러리입니다. 많은 수고를 들이지 않고도 복잡한 커뮤니케이션 시스템을 설계할 수 있도록 해줍니다. ZeroMQ는 스스로 효율적으로 설명하기 위해 지금까지 많은 노력을 해왔습니다. 처음에는 '메세징 미들웨어'로 소개되었지만, 나중에는 '스테로이드를 맞은 TCP' 그리고 나중에는 '네트워크 스택의 새로운 레이어'라고 말합니다.


ZeroMQ(oMQ, zmq)는 임베디드 네트워킹 라이브러리 이지만, 동시성 프레임워크와 같은 역할을 합니다.


ZeroMQ는 in-process, inter-process, TCP, and multicast 처럼 다양한 방식으로 메시지를 전송하는 소켓을 제공합니다. fanout, pub-sub, tsak distribution, and request-reply와 같은 패턴으로 N-to-N 소켓을 연결할 수 있습니다. 클러스터 구조에서 충분한 속도를 제송합니다. 또한, 비동기 I/O 모델은 비동기 메시지 처리를 제공하는 확장 멀티 코어 애플리케이션을 제공합니다. 이것은 language API를 제공하며 대부분의 운영 체제에서 실행됩니다. ZeroMQ는 iMatix에서 만들어졌으며, LGPL 오픈소스 소프트웨어입니다.


퍼포먼스

ZeroMQ는 정말 빠릅니다. 대부분의 AMQP들 보다 단위가 다를 정도로 빠릅니다. 이러한 퍼포먼스는 다음과 같은 기술을 보유하기 있기때문에 가능한 것입니다.


 - AMQP처럼 과도하게 복잡한 프로토콜이 없습니다.

 - 신뢰성 있는 멀티캐스트나 Y-suite IPC 전송같은 효율적인 전송을 활용합니다.

 - 지능적인 메시지 묶음을 활용합니다. 이것은 ZeroMQ로 하여금 프로토콜 오버헤드뿐만 아니라 시스템 호출을 줄여서 TCP/IP를 효율적으로 사용하게 합니다.


단순성

API는 믿을 수 없을 정도로 간단합니다. 그렇기에 소켓 버퍼에 계속 '값을 채워' 주어야 하는 소켓 방식에 비교하면 메시지를 보내는 것이 정말로 단순합니다. ZeroMQ에서는 그냥 비동기 send 호출을 부르기만 하면, 메시지를 별도의 스레드의 큐에 넣고 필요한 모든 일을 해줍니다. 이러한 비동기 특성이 있기에 애플리케이션은 메시지가 처리되기를 기다리며 시간을 낭비하지 않아도 됩니다. ZeroMQ의 비동기 특성은 이벤트 중심의 프레임워크에도 최적입니다.


ZeroMQ의 단순한 와이어 프로토콜은 다양한 전송 프로토콜이 사용되는 요즘에 적합합니다. AMQP를 쓴다면 그 위에 또 다른 프로토콜 레이어를 쓴다는 것은 좀 이상하게 느껴집니다. ZeroMQ는 메세지를 그냥 BLOB(Binary Large Object)으로 보기에 메시지 인코딩 방식을 상관하지 않습니다. 단순히 JSON 메세지들을 보내던지, 아니면 BSON, Protocol Buffers나 Thrift 같은 바이너리 방식 메시지들도 괜찮습니다. 


확장성

ZeroMQ 소켓들은 저수준처럼 보이지만 사실은 다양한 기능들을 제공합니다. 예를 들어 하나의 ZeroMQ 소켓은 복수의 접점을 가질 수 있으며 그들 간에 자동으로 메시지 부하 분산을 수행합니다. 또는 하나의 소켓으로 복수의 소스에서 메시지들을 받아들이는 게이트 역할을 할 수도 있습니다.



Kafka

대용량의 실시간 로그 처리에 특화되어 설계된 메시징 시스템으로써 기존 범용 메시징 시스템대비 TPS가 매우 우수합니다. 단, 특화된 시스템이기 때문에 범용 메시징 시스템에서 제공하는 다양한 기능들은 제공되지 않습니다.


분산 시스템을 기본으로 설계되었기 때문에, 기존 메시징 시스템에 비해 분산 및 복제 구성을 손쉽게 할 수 있습니다.


AMQP 프로토콜이나 JMS API를 사용하지 않고 단순한 메시지 헤더를 지닌 TCP 기반의 프로토콜을 사용하여 프로토콜에 의한 오버헤드를 감소시켰씁니다.


Producer가 broker에게 다수의 메시지를 전송할 때 각 메시지를 개별적으로 전송해야하는 기존 메시징 시스템과는 달리, 다수의 메시지를 batch형태로 broker에게 한번에 전달할 수 있어 TCP/IP 라운드트립 횟수를 줄일 수 있습니다.


메시지를 기본적으로 메모리에 저장하는 기존 메시징 시스템과는 달리 메시지를 파일 시스템에 저장합니다.


- 파일 시스템에 메시지를 저장하기 때문에 별도의 설정을 하지 않아도 데이터의 영속성(durabiility)이 보장됩니다.


- 기존 메시징 시스템에서는 처리되지 않고 남아있는 메시지의 수가 많을 수록 시스템의 성능이 크게 감소하였으나, Kafka에서는 메시지를 파일 시스템에 저장하기 때문에 메시지를 많이 쌓아두어도 성능이 크게 감소하지 않습니다. 또한 많은 메시지를 쌓아둘 수 있기 때문에, 실시간 처리뿐만 아니라 주기적인 batch 작업에 사용할 데이터를 쌓아두는 용도로도 사용할 수 있습니다.


- Consumer에 의해 처리된 메시지(acknowledged message)를 곧바로 삭제하는 기존 메시징 시스템과는 달리 처리된 메시지를 삭제하지 않고 파일 시스템에 그대로 두었다가 설정된 수명이 지나면 삭제하도록 처리합니다. 처리된 메시지를 일정 기간동안 삭제하지 않기 때문에 메시지 처리 도중 문제가 발생하였거나 처리 로직이 변경되었을 경우 consumer가 메시지를 처음부터 다시 처리(rewind)하도록 할 수 있습니다.


기존의 메시징 시스템에서는 broker가 consumer에게 메시지를 push해 주는 방식인데 반해, Kafka는 consumer가 broker로부터 직접 메시지를 가지고 가는 pull 방식으로 동작합니다. 따라서 consumer는 자신의 처리능력만큼의 메시지만 broker로부터 가져오기 때문에 최적의 성능을 낼 수 있습니다.


- 기존의 push 방식의 메시징 시스템에서는 broker가 직접 각 consumer가 어떤 메시지를 처리해야 하는지 계산하고 어떤 메시지를 처리 중인지 트랙킹하였다면, Kafka에서는 consumenr가 직접 필요한 메시지를 broker로 부터 pull하므로 broker의 consumer와 메시지 관리에 대한 부담이 경감되었습니다.


- 메시지를 pull 방식으로 가져오므로, 메시지를 쌓아두었다가 주기적으로 처리하는 batch consumer의 구현이 가능합니다.


큐의 기능은 기존의 JMS의 AMQP 기반의 RabbitMQ(데이터 기반 라우팅, 페데레이션 기능 등)등에 비해서는 많이 부족하지만 대용량 메세지를 지원할 수 있는 것이 가장 큰 특징입니다. 특히 분산환경에서 용량 뿐 아니라, 복사본을 다른 노드에 저장함으로써 노드 장애에 대한 장애 대응성을 가지고 있기 때문에 용량에는 확실하게 강점을 지니고 있습니다.



MQ별 Producer 성능

붉은 색 그래프는 메시지를 한 번에 50개씩 batch로 전송한 결과이며, 연두색 그래프는 한 번에 하나씩 전송한 결과입니다.



MQ별 Consumenr 성능

Kafka의 경우 10만 TPS 이상의 성능을 RabbitMQ는 2만 TPS 정도의 성능을 내는 것으로 나와있는데, 여기서 생각해볼 문제가 큐는 비동기 처리 솔루션이라는 것입니다. 따라서 응답시간에는 그렇게 민감하지 않는점을 알 수 있습니다.


그리고 일반적인 웹 시스템의 성능이 1500~2000 TPS (엔터프라이즈 시스템의 경우) 내외인 것이 일반적이기 때문에, RabbitMQ의 2만 TPS의 성능은 충분하다고 볼 수 있습니다.


블로그 저자는 대용량 CEP 엔진에서는 Kafka를 사용해서 처리하고 그이외에 자잘한 메시지큐는 다양한 기능을 제공하는 ActiveMQ를 사용하는 것을 권장하고 있습니다.



Spring에서는 Srping AMQP가 서브 프로젝트로 존재하며 기본 예제는 RabbitMQ로 되어 있습니다. 최근에 AMQP는 아니지만 빠른 속도를 내세우며 엄청난 인기몰이를 하는 Kafka가 있는데 Spring 서브 프로젝트 Spring Kafka도 있어서 빠른 메시징 큐 사용시에 활용 가능합니다.


Kafka의 인기몰이 중 하나는 메시징 전송시에 헤더의 크기가 작아 오버헤드를 감소시키며 기존 메시징 시스템에서는 broker가 consumer에게 메시지를 push해주는 방식인데 반해, Kafka는 consumer가 broker로부터 직접 메시지를 가지고 가는 pull 방식으로 동작합니다. 따라서 consumer는 자신의 처리능력만큼의 메시지만 broker로 부터 가져오기 때문에 최적의 성능을 낼 수 있습니다.



각각 오픈소스 메시징 큐의 지원 프로토콜과 지원하는 클라이언트를 나타낸 표입니다.









태그