Ruby 使用bunny gem,如何阻止,直到收到消息或超时过期

Ruby 使用bunny gem,如何阻止,直到收到消息或超时过期,ruby,rabbitmq,bunny,Ruby,Rabbitmq,Bunny,我正在使用向rabbitmq服务器发送和接收消息。如何在设置等待时间超时的同时同步弹出队列中的消息(即,如果3秒后没有消息到达,请停止阻止) 一个显而易见的解决方案是在超时时间结束或收到消息之前循环pop调用,但这似乎效率很低。有更优雅的解决方案吗?我查看了bunny的文档以及rabbitmq站点上的教程,但我没有找到适合此特定场景的解决方案。为了使此功能成为一种功能,我不得不重写基本方法subscribe。我发现我们可以为通道设置超时时间,但函数中没有这样的输入参数 response = ni

我正在使用向rabbitmq服务器发送和接收消息。如何在设置等待时间超时的同时同步弹出队列中的消息(即,如果3秒后没有消息到达,请停止阻止)


一个显而易见的解决方案是在超时时间结束或收到消息之前循环pop调用,但这似乎效率很低。有更优雅的解决方案吗?我查看了bunny的文档以及rabbitmq站点上的教程,但我没有找到适合此特定场景的解决方案。

为了使此功能成为一种功能,我不得不重写基本方法subscribe。我发现我们可以为通道设置超时时间,但函数中没有这样的输入参数

response = nil

subscribe(block: true, timeout: 10) do |delivery_info, properties, payload|
  Rails.logger.info "got message #{payload}"
  response = payload
  @channel.consumers[delivery_info.consumer_tag].cancel
end



def subscribe(opts = {block: false}, &block)
    ctag = opts.fetch(:consumer_tag, @channel.generate_consumer_tag)
    consumer = Bunny::Consumer.new(@channel,@response_queue,ctag)

    consumer.on_delivery(&block)
    @channel.basic_consume_with(consumer)

    if opts[:block]
      @channel.work_pool.join(opts[:timeout])
    end
end

我没有找到一种使用Bunny轻松实现的方法,我在这里提出的建议没有超时。但它确实支持每次调用检索一条消息的语义。鉴于Bunny在内部使用线程池来接收消息,我认为一种更简单的方法可能是使用阻塞队列,如Ruby的
queue
类,将消息从Bunny的线程池传输到调用线程。如下所示:

# Set up your internal queue somewhere (in your class's initialize maybe?)
@internal_queue = Queue.new

# In the main thread that needs to block
...
# the call to subscribe is non-blocking
queue.subscribe do |delivery_info, properties, payload|
  @internal_queue.enq(payload)  # this runs inside Bunny's pool
end

# the call to deq is blocking
response = @internal_queue.deq  # this blocks the main thread till a
                                # message is pushed to the internal_q
您可以为需要监听的每个AMQP频道维护一个@internal_队列。您可以将这些部分分解成单独的方法,并创建一个整洁的阻塞API,一次返回一条消息


后来,我创建了一个TimedWaitableQueue类,该类封装了一个简单的数组,该数组使用monitor MonitorMixin扩展,然后使用互斥+条件变量语义。这允许阻塞超时的出列调用。

作为@Ilya对上述代码的一个细微变化:我发现我必须创建一个线程超时,然后关闭通道的工作池

module Bunny
  class Queue
    def subscribe(opts = { block: false, timeout: 1000 }, &block)
      ctag = opts.fetch(:consumer_tag, @channel.generate_consumer_tag)
      consumer = Consumer.new(@channel, self, ctag)
      consumer.on_delivery(&block)
      @channel.basic_consume_with(consumer)
      if opts[:block]
        Thread.new do
          sleep(opts[:timeout]/1000.0)
          @channel.work_pool.shutdown
        end
        @channel.work_pool.join
      end
    end
  end
end