Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

go

rabbitmq

amqp

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:

"In almost all cases, using Channel.Consume will be preferred."

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?

like image 806
pymd Avatar asked Jun 18 '13 07:06

pymd


2 Answers

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 <-chan amqp.Delivery, done chan error) {
    select {
    case d = <-deliveries:
        // Do stuff with the delivery
        // Send any errors down the done chan. for example:
        // done <- err
    default:
        done <- nil
    }
}
like image 151
Intermernet Avatar answered Nov 06 '22 08:11

Intermernet


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
  2. 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

like image 6
pinepain Avatar answered Nov 06 '22 08:11

pinepain