如何在 Rails 中使用 RabbitMQ 实现 RPC?

How to implement RPC with RabbitMQ in Rails?

我想实现一个使用 RabbitMQ 调用远程服务并显示返回数据的操作。我以类似于从这里获取的示例的方式实现了这个(到目前为止更多地作为概念证明):https://github.com/baowen/RailsRabbit 它看起来像这样:

控制器:

def rpc
  text = params[:text]
  c = RpcClient.new('RPC server route key')
  response = c.call text
  render text: response
end

RabbitMQ RPC 客户端:

class RpcClient < MQ
  attr_reader :reply_queue
  attr_accessor :response, :call_id
  attr_reader :lock, :condition

  def initialize()
    # initialize exchange:

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

    @x = ch.default_exchange
    @reply_queue = ch.queue("", :exclusive => true)
    @server_queue = 'rpc_queue'

    @lock = Mutex.new
    @condition = ConditionVariable.new
    that = self

    @reply_queue.subscribe do |_delivery_info, properties, payload|
      if properties[:correlation_id] == that.call_id
        that.response = payload.to_s
        that.lock.synchronize { that.condition.signal }
      end
    end
  end

  def call(message)
    self.call_id = generate_uuid
    @x.publish(message.to_s,
               routing_key: @server_queue,
               correlation_id: call_id,
               reply_to: @reply_queue.name)

    lock.synchronize { condition.wait(lock) }
    response
  end

  private

  def generate_uuid
    # very naive but good enough for code
    # examples
    "#{rand}#{rand}#{rand}"
  end
end

一些测试表明这种方法有效。另一方面,这种方法假设为针对此操作的每个请求创建一个客户端(并订阅队列),根据 RabbitMQ tutorial,这是低效的。所以我有两个问题:

  1. 是否可以避免为每个 Rails 请求创建队列?
  2. 这种方法(使用线程和互斥量)将如何干扰我的整个 Rails 环境?在 Rails 中以这种方式实现事情安全吗?如果相关的话,我正在使用 Puma 作为我的网络服务器。

Is it possible to avoid creating a queue for every Rails request?

是的 - 不需要每个请求都有自己的回复队列。

您可以使用内置的直接回复队列。参见 the documentation here

如果您不想使用直接回复功能,您可以为每个 rails 实例创建一个回复队列。您可以使用单个回复队列,并让相关 ID 帮助您确定回复需要在 rails 实例中的哪个位置。

How will this approach (with threads and mutex) interfere with my whole Rails environment? Is it safe to implement things this way in Rails?

此代码中锁/互斥锁的用途是什么?对我来说似乎没有必要,但我可能遗漏了一些东西,因为我已经有 5 年没做过 ruby :)