티스토리 뷰
[RabbitMQ] Topics
(using the Bunny client)
이전 강의에서 우리의 로그 시스템을 발전 시켜봤습니다. 단순히 브로드캐스팅을 하는 fanout exchange 대신에 direct 를 이용하여 메시지 로그를 선택적으로 수신할 수 있게 해보았습니다.
비록 direct exchange를 사용하여 우리의 시스템을 진보시켜보았지만 여전히 다중 기준에 의한 라우팅을 할 수 없는 한계가 존재합니다.
우리의 로그 시스템은 info, warning, error 와 같은 규칙에 분류한 구독을 할 수 있지만 로그를 출력하는 프로그램에 따른 구독은 할 수 없습니다. 유닉스의 syslog 툴의 개념을 생각하면 됩니다. syslog는 규칙(info/warn/cri..)와 출처(auth/cron/kern..) 둘 다에 기반한 라우팅을 지정할 수 있습니다.
이는 엄청난 유연성을 가져다 줍니다. 예를들어, cron 으로부터 오는 메시지들에서는 critical, errors 메시지를, kern 으로부터 오는 메시지들에 대해서는 모든 메시지들을 수신하게 할 수 있습니다.
이를 우리 로그 시스템에 시행하기 위해선 더 복잡한 topic exchange 에 대해 배울 필요가 있습니다.
전제조건
이 튜토리얼에서 RabbitMQ가 localhost에 설치되어있고 기본 포트인 5672로 동작 중인 것을 가정으로 진행됩니다. 다른 호스트나, 포트를 사용할 경우 적절한 세팅을 하셔야합니다.
Topic exchange
topic exchange를 이용하여 메시지를 보낼 때는 routing_key를 마음대로 정할 수 없습니다. 단어와 범위표시자를 점을 구분으로한 리스트여야만 합니다. 단어는 아무것이나 할 수 있지만, 보통 해당 메시지의 특징을 나타내는 것으로 합니다. 몇 개의 가능한 예제입니다: "stock.usd.nyse", "nyse.vmw", "quick.orange.rabbit". routing key로 255byte 이하의 단어들을 사용할 수 있습니다.
binding key 도 같은 양식을 따릅니다. topic exchange의 로직은 direct와 비슷합니다. 메시지는 특정 routing key와 보내지고 binding key와 일치하는 모든 queue에게 전송됩닏나. 하지만 binding key에 중요하고 특별한 두 경우가 있습니다.
> *(별)은 단어 한 단어를 대체합니다.
> #(해쉬)는 0 단어 이상을 대체합니다.
이를 쉽게 표현한 예제입니다.
이 예제에서 동물들을 묘사하는 메시지를 보낼 것 입니다. 메시지들은 3개의 단어들(2개의 점)로 구성된 routing key가 적용됩니다. routing key의 첫 번째 단어는 속도를, 두 번째는 색깔을, 세 번째는 종을 묘사합니다. "<speed>.<colour>.<species>"
저희는 세 개의 binding을 만들 것 입니다. Q1는 "*.orange.*" , Q2는 "*.*.rabbit"과 "lazy.#" binding key에 bind 됩니다.
이 binding 들을 요약하자면:
> Q1는 orange 동물들에 대해 수신합니다.
> Q2는 모든 rabbit 들 혹은 lazy 동물들에 대해 수신합니다.
"quick.orange.rabbit"의 경우 두 queue에 모두 전달됩니다. "lazy.orange.elephant" 또한 두 queue 모두에 전달되게 됩니다. 한편으로 "quick.orange.fox"의 경우 Q1만, "lazy.brown.fox"의 경우 Q2에만 전달되게 됩니다. "lazy.pink.rabbit"은 Q2의 두 binding과 일치하지만 한번만 Q2에 전달되게 됩니다. "quick.brown.fox"는 어느 binding에도 일치하지 않으므로 버려지게 됩니다.
만약 "orange"나 "quick.orange.male.rabbit"과 같이 한 단어나 4개의 단어로 이루어진 key로 메시지를 보내면 어떻게 될까요? 이 경우 메시지는 어느 binding과 일치하지 않아 버려지게 됩니다.
한편 "lazy.orange.male.rabbit"의 경우 4 단어이지만 lazy.#와 일치하게 되어 Q2로 보내지게 됩니다.
Topic exchange
Topic exchange는 강력하고 또한 다른 exchange의 기능들을 구현할 수 있습니다.
queue가 "#"(hash) binding key로 bind 되어있을 때 routing key와 상관없이 모든 메시지들을 수령하게 됩니다. - fanout exchange와 같이
"*"(star)이나 "#"(hash)가 binding에 사용되지 않으면 topic exchange는 direct와 같이 사용됩니다.
종합하기
저희의 로그 시스템에 topic exchange를 사용할 것 입니다. routing key가 두 단어를 가진다고 가정합니다: "<facility>.<severity>"
이 코드에서 대부분은 이전 강의에서 사용했던 것 입니다.
emit_log_topic.rb 코드입니다:
#!/usr/bin/env ruby # encoding: utf-8 require "bunny" conn = Bunny.new conn.start ch = conn.create_channel x = ch.topic("topic_logs") severity = ARGV.shift || "anonymous.info" msg = ARGV.empty? ? "Hello World!" : ARGV.join(" ") x.publish(msg, :routing_key => severity) puts " [x] Sent #{severity}:#{msg}" conn.close
receive_logs_topic.rb 코드입니다:
#!/usr/bin/env ruby # encoding: utf-8 require "bunny" if ARGV.empty? abort "Usage: #{$0} [binding key]" end conn = Bunny.new conn.start ch = conn.create_channel x = ch.topic("topic_logs") q = ch.queue("", :exclusive => true) ARGV.each do |severity| q.bind(x, :routing_key => severity) end puts " [*] Waiting for logs. To exit press CTRL+C" begin q.subscribe(:block => true) do |delivery_info, properties, body| puts " [x] #{delivery_info.routing_key}:#{body}" end rescue Interrupt => _ ch.close conn.close end
모든 로그를 수신하려면:
$ ruby -rubygems receive_logs_topic.rb "#"
"kern" facility 의 모든 로그를 수신하려면:
$ ruby -rubygems receive_logs_topic.rb "kern.*"
오직 "critical" 로그만 수신하려면:
$ ruby -rubygems receive_logs_topic.rb "*.critical"
다중 binding을 생성하려면:
$ ruby -rubygems receive_logs_topic.rb "kern.*" "*.critical"
"kern.critical" 타입의 로그를 만드려면:
$ ruby -rubygems emit_log_topic.rb "kern.critical" "A critical kernel error"
다음 강의에서는 원격 프로시져 호출과 같은 round trip 메시지에 대해 살펴보겠습니다.