我的第一个问题确实是设计问题。这是我第一次编写使用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队列出队(如上面的代码所示)。我不确定为什么也会发生这种情况?
相关代码链接:
答案 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)