2014-05-03 11 views
5

Ich integriere Bunny Edelstein für RabbitMQ mit Rails, sollte ich Bunny-Thread in einem Initialisierer starten, dass Rails startet mit Anwendung starten oder es in einer separaten Rake-Aufgabe tun, damit ich es starten kann in einem separaten Prozess?So starten Sie Hase-Thread in Rails und Thin

Ich denke, wenn ich Nachrichten nur dann erzeuge, muss ich es in Rails Initialisierer tun, damit es überall in der App verwendet werden kann, aber wenn ich konsumiere, sollte ich es in einer separaten Rake-Aufgabe tun, ist das korrekt ?

Antwort

5

Sie haben Recht: Sie sollten nicht von der Rails-Anwendung selbst verbrauchen. Die Rails-Anwendung sollte ein Producer sein. In diesem Fall ist ein Initialisierer der richtige Ort, um die Bunny-Instanz zu starten.

Ich habe im Wesentlichen diesen Code in meiner Rails-Anwendungen, die Nachrichten zu RabbitMQ veröffentlichen:

# 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 

Für Nachrichten raubend, ziehe ich beteiligt ausführbare Dateien ohne Rake zu starten. Rake wird einen neuen Prozess erstellen, der mehr Speicher benötigt.

# 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 

alles, was allerdings angesichts Sie besser dran, mit Edelsteinen vorgefertigter sein können und Abstraktion mit Rails, ActiveJob.

+0

Warum rufen Sie das zweite Mal 'MESSAGING_SERVICE.start' an? Es wurde bereits in 'MessagingService' gestartet. – sekrett