2014-05-03 13 views
5

Sto integrando Bunny gem per RabbitMQ con Rails, dovrei avviare Bunny thread in un inizializzatore che Rails inizia con l'avvio dell'applicazione o lo esegue in un rake task separato in modo da poterlo avviare in un processo separato?Come avviare il thread del coniglio in Rails and Thin

Penso che se produco messaggi solo dopo ho bisogno di farlo nell'inizializzatore Rails in modo che possa essere utilizzato su tutta l'app, ma se sto consumando dovrei farlo in un task rake separato, è corretto ?

risposta

5

Si è corretto: non si dovrebbe consumare dall'applicazione Rails stessa. L'applicazione Rails dovrebbe essere un produttore, nel qual caso, un inizializzatore è il punto corretto per avviare l'istanza Bunny.

ho essenzialmente hanno questo codice nel mio applicazioni Rails che pubblicano messaggi a RabbitMQ:

# config/initializers/bunny.rb 
MESSAGING_SERVICE = MessagingService.new(ENV.fetch("AMQP_URL")) 
MESSAGING_SERVICE.start 

# app/controllers/application_controller.rb 
class ApplicationController 
    def messaging_service 
    MESSAGING_SERVICE 
    end 
end 

# app/controllers/uploads_controller.rb 
class UploadsController < ApplicationController 
    def create 
    # save the model 
    messaging_service.publish_resize_image_request(model.id) 
    redirect_to uploads_path 
    end 
end 

# lib/messaging_service.rb 
class MessagingService 
    def initialize(amqp_url) 
    @bunny = Bunny.new(amqp_url) 
    @bunny.start 
    at_exit { @bunny.stop } 
    end 

    attr_reader :bunny 

    def publish_resize_image_request(image_id) 
    resize_image_exchange.publish(image_id.to_s) 
    end 

    def resize_image_exchange 
    @resize_image_exchange ||= 
     channel.exchange("resize-image", passive: true) 
    end 

    def channel 
    @channel ||= bunny.channel 
    end 
end 

Per il consumo di messaggi, preferisco iniziare eseguibili senza Rake coinvolti. Rake forgerà un nuovo processo, che utilizzerà più memoria.

# bin/image-resizer-worker 
require "bunny" 
bunny = Bunny.new(ENV.fetch("AMQP_URL")) 
bunny.start 
at_exit { bunny.stop } 

channel = bunny.channel 

# Tell RabbitMQ to send this worker at most 2 messages at a time 
# Else, RabbitMQ will send us as many messages as we can absorb, 
# which would be 100% of the queue. If we have multiple worker 
# instances, we want to load-balance between each of them. 
channel.prefetch(2) 

exchange = channel.exchange("resize-image", type: :direct, durable: true) 
queue = channel.queue("resize-image", durable: true) 
queue.bind(exchange) 
queue.subscribe(manual_ack: true, block: true) do |delivery_info, properties, payload| 
    begin 
    upload = Upload.find(Integer(payload)) 
    # somehow, resize the image and/or post-process the image 

    # Tell RabbitMQ we processed the message, in order to not see it again 
    channel.acknowledge(delivery_info.delivery_tag, false) 

    rescue ActiveRecord::RecordNotFound => _ 
    STDERR.puts "Model does not exist: #{payload.inspect}" 
    # If the model is not in the database, we don't want to see this message again 
    channel.acknowledge(delivery_info.delivery_tag, false) 

    rescue Errno:ENOSPC => e 
    STDERR.puts "Ran out of disk space resizing #{payload.inspect}" 
    # Do NOT ack the message, in order to see it again at a later date 
    # This worker, or another one on another host, may have free space to 
    # process the image. 

    rescue RuntimeError => e 
    STDERR.puts "Failed to resize #{payload}: #{e.class} - #{e.message}" 
    # The fallback should probably be to ack the message. 
    channel.acknowledge(delivery_info.delivery_tag, false) 
    end 
end 

dato tutto ciò, però, potrebbe essere meglio con gemme pre-costruito e utilizzando l'astrazione Rails', ActiveJob.

+0

Perché stai chiamando 'MESSAGING_SERVICE.start' per la seconda volta? È già stato avviato in 'MessagingService'. – sekrett

Problemi correlati