5

I'm evaluating RabbitMQ and while the general impression (of AMQP as such, and also RabbitMQ) is positive, I'm not very impressed by the result.

I'm attempting to publish and consume messages simultaneously and have achieved very poor message rates. I have a durable direct exchange, which is bound to a durable queue and I publish persistent messages to that exchange. The average size of the message body is about 1000 bytes.

My publishing happens roughly as follows:

AMQP.BasicProperties.Builder bldr = new AMQP.BasicProperties.Builder();
ConnectionFactory factory = new ConnectionFactory();
factory.setUsername("guest");
factory.setPassword("guest");
factory.setVirtualHost("/");
factory.setHost("my-host");
factory.setPort(5672);
Connection conn = null;
Channel channel = null;
ObjectMapper mapper = new ObjectMapper(); //com.fasterxml.jackson.databind.ObjectMapper
try {
    conn = factory.newConnection();
channel = conn.createChannel();
    channel.confirmSelect();
} catch (IOException e) {}

for(Message m : messageList) { //the size of messageList happens to be 9945
    try {
        channel.basicPublish("exchange", "", bldr.deliveryMode(2).contentType("application/json").build(), mapper.writeValueAsBytes(cm));
    } catch (Exception e) {}
}
try {
    channel.waitForConfirms();
    channel.close();
conn.close();
} catch (Exception e1) {}               

And consuming messages from the bound queue happens as so:

AMQP.BasicProperties.Builder bldr = new AMQP.BasicProperties.Builder();
ConnectionFactory factory = new ConnectionFactory();
factory.setUsername("guest");
factory.setPassword("guest");
factory.setVirtualHost("/");
factory.setHost("my-host");
factory.setPort(5672);
Connection conn = null;
Channel channel = null;
try {
    conn = factory.newConnection();
    channel = conn.createChannel();
    channel.basicQos(100);
    while (true) {
        GetResponse r = channel.basicGet("rawDataQueue", false);
        if(r!=null)
            channel.basicAck(r.getEnvelope().getDeliveryTag(), false);
    }
} catch (IOException e) {}

The problem is that when the message publisher (or several of them) and consumer (or several of them) run simultaneously then the publisher(s) appear to run at full throttle and the RabbitMQ management web interface shows a publishing rate of, say, ~2...3K messages per second, but a consumption rate of 0.5...3 per consumer. When the publisher(s) finish then I get a consumption rate of, say, 300...600 messages per consumer. When not setting the QOS prefetch value for the Java client, then a little less, when setting it to 100 or 250, then a bit more.

When experimenting with throttling the consumers somewhat, I have managed to achieve simultaneous numbers like ~400 published and ~50 consumed messages per second which is marginally better but only marginally.

Here's, a quote from the RabbitMQ blog entry which claims that queues are fastest when they're empty which very well may be, but slowing the consumption rate to a crawl when there are a few thousand persistent messages sitting in the queue is still rather unacceptable.

Higher QOS prefetching values may help a bit but are IMHO not a solution as such.

What, if anything, can be done to achieve reasonable throughput rates (2 consumed messages per consumer per second is not reasonable in any circumstance)? This is only a simple one direct exchange - one binding - one queue situation, should I expect more performance degradation with more complicated configurations? When searching around the internet there have also been suggestions to drop durability, but I'm afraid in my case that is not an option. I'd be very happy if somebody would point out that I'm stupid and that there is an evident and straightforward solution of some kind :)

Community
  • 1
  • 1
Manjabes
  • 1,884
  • 3
  • 17
  • 34

2 Answers2

11

Have you tried with the autoAck option? That should improve your performance. It is much faster than getting the messages one by one and ack'ing them. Increasing the prefetch count should make it even better too.

Also, what is the size of the messages you are sending and consuming including headers? Are you experiencing any flow-control in the broker?

Another question, are you creating a connection and channel every time you send/get a message? If so, that's wrong. You should be creating a connection once, and use a channel per thread (probably in a thread-local fashion) to send and receive messages. You can have multiple channels per connection. There is no official documentation about this, but if you read articles and forums this seems to be the best performance practice.

Last thing, have you considered using the basicConsume instead of basicGet? It should also make it faster.

Based on my experience, I have been able to run a cluster sending and consuming at rates around 20000 messages per second with non-persistent messages. I guess that if you are using durable and persistent messages the performance would decrease a little, but not 10x.

wonea
  • 4,783
  • 17
  • 86
  • 139
hveiga
  • 6,725
  • 7
  • 54
  • 78
  • How should I determine if the broker applies any flow control? The average message size is 1000-something bytes in the body, plus probably < 64B in headers. basicConsume vs basicGet is something I have to try. Thanks! – Manjabes Dec 12 '13 at 06:47
  • OK, using [QueueingConsumer](http://www.rabbitmq.com/releases/rabbitmq-java-client/current-javadoc/com/rabbitmq/client/QueueingConsumer.html) with explicit acknowledgements I achieve rates of about 2500 pubs/250 subs simultaneously, which is a lot better than before, but not perfect. – Manjabes Dec 12 '13 at 10:17
  • Can you try again removing the `channel.confirmSelect();` and `channel.waitForConfirms();` from your publishing code? – hveiga Dec 12 '13 at 15:49
  • Did so and achieved spikes of 12+K msgs/sec for publishing and 900...2.5K for consuming. The results are, however, distorted by the fact that the queue sits on the other end of a 100M network link and that seemed to be saturated by the publishers. – Manjabes Dec 13 '13 at 08:24
  • 1
    Ok, now that was a clue that led somewhere: when I installed and ran the MQ from my dev machine where the producers and consumer were running, I achieved simultaneous publish/consumption rates of about 2K msg/sec. – Manjabes Dec 13 '13 at 09:14
-2

Operating system could schedule your process to the next time slot, if sleep is used. This could create significant performance decrease.

Secult
  • 1
  • 1