Search code examples
ruby-on-railsasynchronousrabbitmqworkling

Phusion Passenger + Workling + RabbitMQ


I am trying to deploy a RoR app that does some asynchronous task. I use workling for that and the message queue is RabbitMQ. This combination worked flawlessly with Starling but we decided to change the MQ for Rabbit. I read somewhere that I should include the following code in my environment.rb

require 'mq' 
if defined?(PhusionPassenger) 
  PhusionPassenger.on_event(:starting_worker_process) do |forked| 
    if forked 
      if EM.reactor_running? 
        EM.stop_event_loop 
        EM.release_machine 
        EM.instance_variable_set( '@reactor_running', false ) 
      end 
      Thread.current[:mq] = nil 
      AMQP.instance_variable_set('@conn', nil) 
    end 
    th = Thread.current 
    Thread.new{ 
      AMQP.connect(:host => 'localhost'){ 
        th.wakeup 
      } 
    } 
    Thread.stop 
  end 
end 

But that now Apache fails completely with message: The server encountered an internal error or misconfiguration and was unable to complete your request


Solution

  • EDIT: I've improved the code below somewhat since posting this. Available here: http://www.hiringthing.com/2011/11/04/eventmachine-with-rails.html

    I just spent a milliioon years trying to get this to work, and finally did. Here is my code:

    require 'amqp'
    module HiringThingEM
      def self.start
        if defined?(PhusionPassenger)
          PhusionPassenger.on_event(:starting_worker_process) do |forked|
          if forked && EM.reactor_running?
              EM.stop
          end
          Thread.new {
          EM.run do
             AMQP.channel ||= AMQP::Channel.new(AMQP.connect(:host=> Q_SERVER, :user=> Q_USER, :pass => Q_PASS, :vhost => Q_VHOST ))
          end
          }
          die_gracefully_on_signal
          end
        end
      end
    
      def self.die_gracefully_on_signal
        Signal.trap("INT")  { EM.stop }
        Signal.trap("TERM") { EM.stop }
      end
    end
    
    HiringThingEM.start
    

    Now I can use:

    EM.next_tick { AMQP.channel.queue(Q_Q).publish("hi mom") }
    

    Inside the controllers of my Rails app.

    Hope this helps someone.