正确的Go / RabbitMQ方法可以“弹出”队列中的一条消息?

时间:2018-12-07 23:27:55

标签: go kubernetes rabbitmq amqp

我的第一个问题确实是设计问题。这是我第一次编写使用Queue的服务,并且对Go还是陌生的。我试图确定我是否应该以这样一种方式编写我的工作程序,即它仅将一条消息弹出队列,进行处理,然后消失。对于像Kubernetes这样的东西,似乎显得微不足道。

或者我应该让一个长寿的工人不断地等待新消息,但是如果它死了(由于错误或意外)而重新启动了该消息?

我问这个问题的原因是,为了实现前者,感觉有点“被黑客攻击”,因为我必须使用streadway/amqp中的通用go AMQP库编写以下内容(阅读评论) :

// Pop will extract a message from the AMQP queue
func (v *Queue) Pop() (data []byte, err error) {
    msgs, err := v.Channel.Consume(
        v.QueueName, // queue
        v.ConsmerID, // consumer
        true,        // auto-ack
        false,       // exclusive
        false,       // no-local
        false,       // no-wait
        nil,         // args
    )
    if err != nil {
        return nil, err
    }

    // We have to use for .. range because Consume returns
    // "<-chan Delivery" but if we only want ONE message popped off
    // we return on the first one
    for data := range msgs {
        return data.Body, nil
    }

    // We should never get this far...
    return nil, errors.New("Something went wrong")
}

此外,在这种情况下<-chan Delivery是什么?您似乎可以插入某种“流”或对象。有没有办法不必为这些数据类型编写for循环?

编辑:我还发现,即使该代码仅执行一次for-loop迭代一次,也似乎该代码将使ENTIRE队列出队(如上面的代码所示)。我不确定为什么也会发生这种情况?

相关代码链接:

1 个答案:

答案 0 :(得分:2)

要简单地从<-chan Delivery中获取单个对象,请勿使用range循环,而应使用channel operator <-

data := <- msgs
return data.Body, nil

关于为什么在提取一条消息后立即清空整个队列:这很可能是由于Consumer prefetch所致。在使用消息时,客户端实际上不会一一从代理中弹出消息,而是以可配置的大小批量(如果我没记错的话,默认情况下大约为32或64条消息)。经纪人将这批消息发布给您的消费者后,它们就会进入您的msgs频道;并且如果您在收到第一条消息后不再从该通道读取信息,其余的将消失(至少在启用auto-ack的情况下,否则,它们将在关闭通道后重新排队)。

要一次仅获取一条消息,请使用频道的QoS function(第一个参数为预取计数):

err := v.Channel.Qos(1, 0, false)