使用channel.Get()相比channel.Consume()有什么缺点吗?

huangapple go评论85阅读模式
英文:

Are there disadvantages of using channel.Get() over channel.Consume()?

问题

我正在使用streadway的amqp库与rabbitmq服务器进行连接。
该库提供了一个channel.Consume()函数,它返回一个"<- chan Delivery"。
它还提供了一个channel.Get()函数,其中包括一个"Delivery"等其他内容。

我需要实现一个pop()功能,并且我正在使用channel.Get()。然而,文档中说:

"在几乎所有情况下,使用Channel.Consume将被优先选择。"

这里的preferred是否意味着recommended?使用channel.Get()而不是channel.Consume()有什么缺点吗?如果有,我如何使用channel.Consume()来实现Pop()函数?

英文:

I'm using streadway's amqp library to connect with a rabbitmq server.
The library provides a channel.Consume() function which returns a "<- chan Delivery".
It also provides a channel.Get() function which returns a "Delivery" among other things.

I've to implement a pop() functionality, and I'm using channel.Get(). However, the documentation says:

&quot;In almost all cases, using Channel.Consume will be preferred.&quot;

Does the preferred here means recommended? Are there any disadvantages of using channel.Get() over channel.Consume()? If yes, how do I use channel.Consume() to implement a Pop() function?

答案1

得分: 6

根据文档,据我所知,“preferred”确实意味着“推荐”。

从文档中可以看出,channel.Get()提供的功能没有channel.Consume()多,并且由于返回的是chan类型的Delivery,而不是每个单独的Delivery,因此在并发代码中更容易使用。

提到的额外功能包括exclusivenoLocalnoWait,以及一个可选的Table参数,它们对队列或服务器具有特定的语义。

要使用channel.Consume()实现一个Pop()函数,你可以参考amqp示例消费者中的一些代码片段,创建一个使用Consume()函数的channel,创建一个处理chan类型的Delivery的函数,该函数实际上会实现你的Pop()功能,然后在一个goroutine中启动handle()函数

关键在于,通道(在示例中)在没有接收方时会阻塞发送。在示例中,handle()函数使用range来处理整个通道,直到它为空。你的Pop()功能可能更适合使用一个函数,该函数只接收chan中的最后一个值并返回它。每次运行时,它都会返回最新的Delivery

编辑:以下是一个示例函数,用于接收通道中的最新值并对其进行处理(这可能对你的用例不起作用,如果该函数将Delivery发送到另一个chan以供另一个函数处理可能更有用。此外,我没有测试下面的代码,可能会有错误)。

func handle(deliveries <-chan amqp.Delivery, done chan error) {
    select {
    case d := <-deliveries:
        // 处理delivery
        // 将任何错误发送到done chan。例如:
        // done <- err
    default:
        done <- nil
    }
}
英文:

As far as I can tell from the docs, yes, "preferred" does mean "recommended".

It seems that channel.Get() doesn't provide as many features as channel.Consume(), as well as being more readily usable in concurrent code due to it's returning a chan of Delivery, as opposed to each individual Delivery separately.

The extra features mentioned are exclusive, noLocal and noWait, as well as an optional Table of args "that have specific semantics for the queue or server."

To implement a Pop() function using channel.Consume() you could, to link to some code fragments from the amqp example consumer, create a channel using the Consume() function, create a function to handle the chan of Delivery which will actually implement your Pop() functionality, then fire off the handle() func in a goroutine.

The key to this is that the channel (in the linked example) will block on sending if nothing is receiving. In the example, the handle() func uses range to process the entire channel until it's empty. Your Pop() functionality may be better served by a function that just receives the last value from the chan and returns it. Every time it's run it will return the latest Delivery.

EDIT: Example function to receive the latest value from the channel and do stuff with it (This may not work for your use case, it may be more useful if the function sent the Delivery on another chan to another function to be processed. Also, I haven't tested the code below, it may be full of errors)

func handle(deliveries &lt;-chan amqp.Delivery, done chan error) {
	select {
	case d = &lt;-deliveries:
		// Do stuff with the delivery
		// Send any errors down the done chan. for example:
		// done &lt;- err
	default:
		done &lt;- nil
	}
}

答案2

得分: 6

这真的取决于你想要做什么。如果你只想从队列中获取一条消息(第一条),你可能应该使用basic.get,如果你计划处理队列中的所有传入消息,那么basic.consume就是你想要的。

可能这不是一个特定于平台或库的问题,而是一个协议理解的问题。

更新

我对Go语言不太熟悉,所以我会尝试给你一些关于AMQP细节的简要说明并描述使用情况。

有时候使用basic.consume可能会遇到一些麻烦和额外的开销:

使用basic.consume的工作流程如下:

  1. 发送basic.consume方法通知代理你想要接收消息
    • 虽然这是一个同步方法,但要等待来自代理的basic.consume-ok消息
  2. 开始监听来自服务器的basic.deliver消息
    • 这是一个异步方法,你需要自己处理服务器上没有可用消息的情况,例如限制读取时间

使用basic.get的工作流程如下:

  1. 发送同步方法basic.get给代理
    • 等待basic.get-ok方法,其中包含消息或basic.empty方法,表示服务器上没有可用的消息

*关于同步和异步方法的说明:*同步方法预期会有一些响应,而异步方法则没有

*关于basic.qos方法的prefetch-count属性的说明:*当在basic.consumebasic.get上设置了no-ack属性时,它会被忽略。

规范对basic.get有一个注释:“该方法提供了一种直接访问队列中消息的方式,使用同步对话,适用于同步功能比性能更重要的特定类型的应用程序”,这适用于连续消息消费。

我的个人测试显示,在RabbitMQ 3.0.1,Erlang R14B04上,使用basic.get连续获取1000条消息(0.38659715652466)比使用basic.consume逐条获取1000条消息(0.47398710250854)平均快15%以上。

如果只在主线程中消费一条消息是你的情况,可能你需要使用basic.get

你仍然可以异步地消费一条消息,例如在单独的线程中或使用某种事件机制。这对于你的机器资源来说可能是更好的解决方案,但你必须注意队列中没有可用消息的情况。

如果你必须逐条处理消息,那么显然应该使用basic.consume,我认为。

英文:

It really depend of what are you trying to do. If you want to get only one message from queue (first one) you probably should use basic.get, if you are planning to process all incoming messages from queue - basic.consume is what you want.

Probably, it is not platform or library specific question but rather protocol understanding question.

UPD

I'm not familiar with it go language well, so I will try to give you some brief on AMQP details and describe use cases.

You may get in troubles and have an overhead with basic.consume sometimes:

With basic.consume you have such workflow:

  1. Send basic.consume method to notify broker that you want to receive messages
  • while this is a synchronous method, wait for basic.consume-ok message from broker
  1. Start listening to basic.deliver message from server
  • this is an asynchronous method and you should take care by yourself situations where no messages on server available, e.g. limit reading time

With basic.get you have such workflow:

  1. send synchronous method basic.get to broker
  • wait for basic.get-ok method, which hold message(s) or basic.empty method, which denote situation no message available on server

Note about synchronous and asynchronous methods: synchronous is expected to have some response, whether asynchronous doesn't

Note on basic.qos method prefetch-count property: it is ignored when no-ack property is set on basic.consume or basic.get.

Spec has a note on basic.get: "this method provides a direct access to the messages in a queue using a synchronous dialogue that is designed for specific types of application where synchronous functionality is more important than performance" which applies for continuous messages consumption.

My personal tests show that getting in row 1000 messages with basic.get (0.38659715652466) is faster than getting 1000 messages with basic.consume one by one (0.47398710250854) on RabbitMQ 3.0.1, Erlang R14B04 in average more than 15%.

If consume only one message in main thread is your case - probably you have to use basic.get.

You still can consume only one message asynchronously, for example in separate thread or use some event mechanism. It would be better solution for you machine resource sometimes, but you have to take care about situation where no message available in queue.

If you have to process message one by one it is obvious that basic.consume should be used, I think

huangapple
  • 本文由 发表于 2013年6月18日 15:44:34
  • 转载请务必保留本文链接:https://go.coder-hub.com/17163252.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定