我正在使用bunny ruby gem向rabbitmq服务器发送和接收消息。如何在我等待的时间超时(例如,如果3秒后没有消息到达,停止阻止)的同时从队列中同步弹出消息?
一个显而易见的解决方案是直接遍历pop调用,直到超时或收到消息,但这似乎非常低效。有更优雅的解决方案吗?我看了一下bunny的文档以及rabbitmq网站上的教程,但是我找不到针对这个特定场景的解决方案。
答案 0 :(得分:1)
要使这样的功能被强制重写基本方法订阅。我发现我们可以为通道设置超时时间,但函数中没有这样的输入参数。
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
答案 1 :(得分:1)
我没有找到一种方法可以轻松地使用Bunny,我在这里建议阻止没有超时。但它确实支持每个调用语义检索一条消息。鉴于Bunny内部使用线程池来接收消息,我认为更简单的方法可能是使用阻塞队列(例如Ruby的Queue
类)来传输来自Bunny的线程的消息-pool到调用线程。如下所示:
# 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_queue。您可以将这些部分分解为单独的方法,并制作一个巧妙的阻止API,一次返回一条消息。
我后来创建了一个TimedWaitableQueue类,它包含一个使用监视器MonitorMixin扩展的简单数组,然后使用互斥+条件变量语义。这允许在超时的呼叫中阻塞。
答案 2 :(得分:0)
@Ilya对上述代码进行了细微改动:https://stackoverflow.com/a/35126963/448858我发现必须创建一个线程来超时,然后关闭通道的工作池
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