티스토리 뷰

RabbitMQ란?



RabbitMQ는 AMQP 프로토콜을 구현한 메시지 브로커입니다. producers 로부터 메시지들을 받아 consumers 에게 전달해주는 서비스로, 쉽게말해 시스템 간에 메시지를 전달해주는 서비스입니다. RabbitMQ는 사용자가 설정한 규칙에 따라 메시지들을 route, buffer, persist 할 수 있습니다.


일반적으로 메시징 시스템이나 RabbitMQ 에서 사용하는 컴퓨터 용어를 살펴보겠습니다.


Producing 은 메시지를 전송한다는 의미입니다. 메시지들을 전송하는 프로그램을 producer 라 부릅니다. 앞으로 producer를 P 라고 나타내겠습니다.



Queue는 mailbox를 의미하며 RabbitMQ 시스템 내에 위치하게 됩니다. 메시지들은 RabbitMQ를 통해 사용자 어플리케이션으로 전송될때 queue 안에 저장되어집니다. Queue는 제한없이 사용자가 원하는 만큼 메시지들을 저장할 수 있습니다(메모리가 충분하다면 무한한 버퍼 용량을 가집니다). 여러 producers 은 하나의 queue 를 통해 메시지를 보낼 수 있고 consumers 들은 이 queue 로 부터 데이터를 받을 수 있습니다. 앞으로 queue를 다음과 같이 나타낼 것입니다.



Consuming은 메시지를 수신한다는 의미입니다. 메시지 수신을 기다리는 프로그램을 consumer라고 합니다. 앞으로 consumer를 C 라고 나타내겠습니다.



보통 대부분의 어플리케이션에서 producer, consumer 와 broker는 같은 물리적 노드에 존재하지 않습니다.


전제조건


이 튜토리얼에서 RabbitMQ가 localhost에 설치되어있고 기본 포트인 5672로 동작 중인 것을 가정으로 진행됩니다. 다른 호스트나, 포트를 사용할 경우 적절한 세팅을 하셔야합니다.


Hello World

(using the Bunny Ruby Client)


이 튜토리얼에서 두 개의 작은 프로그램을 Ruby 언어로 프로그래밍할 것입니다 (Producer는 단일 메시지를 보내고 consumer는 메시지를 수신하여 출력하는 프로그램입니다). Bunny API의 상세한 내용들을 넘어가고 RabbitMQ를 입문하기 위한 기본적인 것들에 초점을 둘것입니다. Messaging 에서의 Hello World 입니다.


아래의 다이어그램에서 P와 C는 우리가 작성할 producer 와 consumer 입니다. 사이의 네모박스는 queue 를 나타냅니다. Queue는 consumer 대신에 RabbitMQ가 보관하는 메시지 버퍼입니다.



The Bunny client library


RabbitMQ 는 상호 정보 교환이 가능한 메시징 프로토콜의 의미를 정의한 명세서 AMQP 0.9.1 버전을 구현한 것입니다. 다양한 언어로 구현된 RabbitMQ 클라이언트 프로그램들이 있습니다. 이 튜토리얼에서는 Ruby 언어로 작성된 Bunny client를 사용할 것 입니다.


우선, Rubygems를 통해 Bunny 를 설치해야 합니다.

$ gem install bunny --version ">= 1.6.0"


Bunny 가 설치되면 이제 코드를 작성할 수 있습니다.



Sending



저희가 만들 메시지 송신 프로그램(송신자)을 send.rb, 메시지 수신 프로그램을 receive.rb라 이름 짓겠습니다. 송신자는 RabbitMQ에 연결하고 단일 메시지를 전송 후 종료될 것입니다.


send.rb에서 우선 라이브러리를 명시해야 합니다.

#!/usr/bin/env ruby

# encoding: utf-8


require "bunny"


그리고 RabbitMQ 서버에 연결을 시킵니다.

conn = Bunny.new

conn.start


connection 은 소켓 연결을 추상화하며 프로토콜 버전, 인증 등을 관리합니다. 여기서는 기본 설정으로 로컬 머신에 있는 브로커에 연결을 할 것입니다.


만약 다른 머신에 있는 브로커에 연결을 하고 싶으면 호스트 이름이나 IP 주소를 :hostname 옵션을 사용하여 명시해주면 됩니다.

conn = Bunny.new(:hostname => "rabbit.local")

conn.start


다음으로 작업을 수행하기 위한 대부분의 API들이 내장되어있는 채널을 생성합니다.

ch = conn.create_channel


전송하기 위해서 queue를 선언해줘야 합니다. 그런다음 queue 로 메시지를 송신하면 됩니다.

q = ch.queue("hello")\

ch.default_exchange.publish("Hello World!", :routing_key => q.name)

puts " [x] Sent 'Hello World!'


큐 선언시 해당 큐가 이미 존재하지 않을 경우에만 새로 생성합니다. 메시지는 바이트 배열로 전송되므로 사용자가 원하는 인코딩작업을 해줘도 상관없습니다.


마지막으로 connection 을 닫으면 됩니다.

conn.close


send.rb 파일의 전체 스크립트입니다.(링크)


송신이 이루어지지 않을 때


만약 RabbitMQ를 사용하는 것이 처음이고 "Sent" 메시지를 볼 수 없다면 무엇이 잘못됬는지 혼란스러울수 도 있습니다. 아마 broker 가 충분한 공간을 가지고 있지 않을 경우일 것 입니다(기본적으로 1Gb 의 여유 공간이 필요합니다). 이 경우 메시지 수신을 거부할 수 있습니다. broker의 로그파일을 확인하고 필요하다면 용량제한을 감소하시면 됩니다. 파일 구성 문서(링크)에서 disk_free_limit 을 설정하는 법을 확인하실 수 있습니다.



Receiving


저희가 앞서 작성한 send.rb 송신자에 맞는 receiver를 작성해보겠습니다. 작성할 receiver는 단일 메시지를 송신한 sender와 달리 RabbitMQ로 부터 메시지를 가져오는 기능을 합니다. 우리는 receiver를 계속 메시지 수신 대기를 하게 하고 수신 시 메시지를 출력하도록 만들 것 입니다.



송신할 때처럼 receive.rb 파일에도 require를 할 필요가 있습니다.

#!/usr/bin/env ruby

# encoding: utf-8


require "bunny"


설정하는 부분은 sender와 같습니다. connection과 channel을 열고 메시지를 수신할 queue를 선언합니다. sender에서 선언한 queue 와 일치하도록 해야합니다.

conn = Bunny.new

conn.start


ch = conn.create_channel

q = ch.queue("hello")


서버에게 queue로 부터 메시지들을 receiver 에게 전달해달라고 해야합니다. 서버는 메시지들을 비동기적으로 전달하기 때문에 RabbitMQ가 receiver에게 메시지들을 전송할때 실행될 callback 함수를 작성해야합니다. 이는 Bunny::Queue#subscribe 에서 이루어집니다.

puts " [*] Waiting for messages in #{q.name}. To exit press CTRL+C"

q.subscribe(:block => true) do |delivery_info, properties, body|

puts " [x] Received #{body}"


# cancel the consumer to exit

delivery_info.consumer.cancel

end


Bunny::Queue#subscribe 는 쓰레드를 호출하는 :block 옵션과 함께 사용됩니다.


receive.rb 파일의 전체 스크립트입니다.(링크)



송/수신자 실행시키기


이제 두 스크립트들을 실행시킬 수 있습니다. 터미널에서 송신자를 실행시키세요.

$ ruby -rubygems send.rb


그리고 수신자를 실행시키세요.

$ ruby -rubygems receive.rb


receiver는 sender가 RabbitMQ를 통해 보낸 메시지를 출력할 것입니다. receiver는 메시지를 계속 수신대기하도록 구동시켜야합니다(멈추기 위해선 Ctrl-C 를 누르면 됩니다). sender는 다른 터미널에서 실행시키면 됩니다.


만약 queue 를 확인하고 싶다면 rabbitmqctl list_queue 를 이용하시면 됩니다.


다음 글에선 간단한 work queue 를 작성하도록 하겠습니다.

댓글