RabbitMQ

다음에 뭔가를 만들면 좀더 그럴싸하게 만들어보고자 !

“웹프론트엔드에서 어떤 요청을 받았는데, 이게 처리가 오래 걸리는 일이다. 이 경우 어떻게 처리하는 것이 좋은가. ” 라는 질문에,

  1. 쓰레드를 생성해서 일을 시키고, 브라우저에는 빨리 응답을 준다.
  2. 프로세스를 실행해서 일을 시키고, 브라우저에는 빨리 응답을 준다.

가 당장 떠오르는 방식인데, 이건 뭐, 날코딩으로 모든걸 해결하겠다는거고, 별문제는 없겠지만 그럴싸해보이질 않는다. 해서, 이런저런 해결책을 뒤져보다가 RabbitMQ가 ruby 코드를 지원한다는 것을 발견. (사실 이미 아주 오래전부터 루비 지원하고 있던 거 같은 느낌이…)

게다가 php 도 지원하니까, 프론트엔드를 “개발자가 넘쳐나는” php로 만들고, 뒷단에 long-running 모듈은 내맘대로 아무거로나 짤 수도 있지않을까 공상을 해보았다. (어차피 내가 앞단 뒷단 다 짜면서..)

$ brew install rabbitmq
$ gem install bunny

설명에 따라 실행을 해주고… http://localhost:15672 에 접속해보아 살아있는지 확인.

아래는 메시지 전송기

require "bunny"

conn = Bunny.new(:automatically_recover => false)
conn.start

ch   = conn.create_channel
q    = ch.queue("backend1")

ch.default_exchange.publish("백엔드 1번 메시지!", :routing_key => q.name)
puts " [x] Sent '백엔드 1번!'"

(1..3).each do |n|
  q    = ch.queue("backend2")
  ch.default_exchange.publish("백엔드 2번 메시지!", :routing_key => q.name)
  puts " [x] Sent '백엔드 2번!'"
end

conn.close

여러 개의 큐로 이런 저런 메시지를 보내본다.

아래는 수신기

# encoding: utf-8
require "bunny"

def hi(name)
  conn = Bunny.new(:automatically_recover => false)
  conn.start

  ch   = conn.create_channel(nil, 8)
  q    = ch.queue(name)#, :exclusive => true)

  @ii=0
  begin
    puts " [*] Waiting for messages for #{name}. To exit press CTRL+C"
    q.subscribe(:block => false) do |delivery_info, properties, body|
      @ii+=1
      puts " [x] Received #{name} #{body} #{@ii}"
      sleep 9
      @ii-=1
      puts " end #{@ii}"
    end
  rescue Interrupt => _
    conn.close

    exit(0)
  end
  return q
end

q = hi("backend1")
hi("backend2")

puts "wating messages"

loop do
  if q.message_count > 0
    msg = q.pop[:payload]
    #puts "Found Message: #{msg}"
  else
    sleep 5
  end
end

소스는

  1. 여기에서 공식홈페이지 튜토리얼에서 시작해서  (기본 sender와 receiver 소스)

  2. bunny 에 thread pool size 를 넣을 수 있다는 것을 찾아내서 넣고,

  3. 여러개의 큐를 하나의 스크립트에서 받게하려고 subscribe(:block=>false) 로 하고, q.pop 으로 데몬흉내