我有一个Go应用程序,它处理来自单个RabbitMQ队列的事件。我使用github.com/streadway/amqp
RabbitMQ客户端库。
Go应用程序会在大约2-3秒内处理所有消息。如果我从内存中获取消息,则可能并行处理约1000条甚至更多的消息。 但是,不幸的是,RabbitMQ性能更差。 因此,我想更快地使用队列中的消息。
因此,问题是:如何使用github.com/streadway/amqp
以最有效的方式使用消息?
据我了解,有两种方法:
设置高预取
https://godoc.org/github.com/streadway/amqp#Channel.Qos。
使用单个消费者goroutine
示例代码:
conn, err := amqp.Dial("amqp://guest:guest@localhost:5672/")
failOnError(err, "Failed to connect to RabbitMQ")
defer conn.Close()
ch, err := conn.Channel()
failOnError(err, "Failed to open a channel")
defer ch.Close()
ch.Qos(
10000, // prefetch count
0, // prefetch size
false, // global
)
msgs, err := ch.Consume(
q.Name, // queue
"", // consumer
false, // NO auto-ack
false, // exclusive
false, // no-local
false, // no-wait
nil, // args
)
for d := range msgs {
log.Printf("Received a message: %s", d.Body)
err:= processMessage(d)
if err != nil {
log.Printf("%s : while consuming task", err)
d.Nack(false, true)
} else {
d.Ack(false)
}
continue // consume other messages
}
但是processMessage
是否会在这里并行调用?
conn, err := amqp.Dial("amqp://guest:guest@localhost:5672/")
failOnError(err, "Failed to connect to RabbitMQ")
defer conn.Close()
var i = 0
for i = 0; i<=100; i++ {
go func(){
ch, err := conn.Channel()
failOnError(err, "Failed to open a channel")
defer ch.Close()
ch.Qos(
10, // prefetch count
0, // prefetch size
false, // global
)
msgs, err := ch.Consume(
q.Name, // queue
"", // consumer
false, // NO auto-ack
false, // exclusive
false, // no-local
false, // no-wait
nil, // args
)
for d := range msgs {
log.Printf("Received a message: %s", d.Body)
err:= processMessage(d)
if err != nil {
log.Printf("%s : while consuming task", err)
d.Nack(false, true)
} else {
d.Ack(false)
}
continue // consume other messages
}
}()
}
但这是RAM友好的方法吗?对于RabbitMQ来说,不是为每个工作人员都产生一个新的渠道会很戏剧性吗?
所以,问题是,哪个变种更好?更好的性能,更好的内存使用率等。
那么,这里RabbitMQ的最佳用法是什么?
更新:目前,我遇到一种情况,当我的工作人员消耗了VPS上的所有RAM并被OOM杀死。我使用第二种方法。因此,就我而言,更好的是在几分钟的工作后能够保持我的工作人员不被OOM杀死的能力。
更新2:nack
是当工作程序无法处理消息时,ack
是非常重要的。必须处理所有消息(其客户分析),但是有时工作人员无法处理它,因此必须nack
消息才能将其传递给其他工作人员(当前,某些用于处理消息的第三方API有时仅返回503状态代码,在这种情况下,消息应传递给其他工作人员或重试)。
因此,不幸的是,不能选择使用auto-ack
。
答案 0 :(得分:0)
我想每个processMessage()
都在一个新的goroutine中运行。
哪个版本更好?
我更喜欢第一个,因为打开/关闭通道有点贵(2 + 2 TCP数据包)。我认为您的OOM问题与gorutine无关,gorutine非常轻巧,仅需5KB左右。因此,问题可能是由您的processMessage()
引起的。
我认为github.com/streadway/amqp
通道消耗操作是thread/gorutine-safe,因此,只要执行一些消耗操作,就可以在goruntine之间共享通道。