One of the aims of ActiveMQ is to be a high performance message bus. This means using a SEDA architecture to perform as much work as possible asynchronously. To be able to achieve high performance it is important to stream messages to consumers as fast as possible so that the consumer always has a buffer of messages, in RAM, ready to process - rather than have them explicitly pull messages from the server which adds significant latency per message.

There is a danger however that this aggressive pushing of messages to the consumers could flood a consumer as typically its much faster to deliver messages to the consumer than it often is to actually process them.

So ActiveMQ uses a prefetch limit on how many messages can be streamed to a consumer at any point in time. Once the prefetch limit is reached, no more messages are dispatched to the consumer until the consumer starts sending back acknowledgements of messages (to indicate that the message has been processed). The actual prefetch limit value can be specified on a per consumer basis.

Its a good idea to have large values of the prefetch limit if you want high performance and if you have high message volumes. If you have very few messages and each message takes a very long time to process you might want to set the prefetch value to 1 so that a consumer is given one message at a time. Specifying a prefetch limit of zero means the consumer will poll for more messages, one at a time, instead of the message being pushed to the consumer.

Specifying the PrefetchPolicy

You can specify an instance of the ActiveMQPrefetchPolicy on an ActiveMQConnectionFactory or ActiveMQConnection. This allows you to configure all the individual prefetch values; as each different quality of service has a different value. e.g.

  • persistent queues (default value: 1000)
  • non-persistent queues (default value: 1000)
  • persistent topics (default value: 100)
  • non-persistent topics (default value: Short.MAX_VALUE -1)

It can also be configured on the connection URI used when establishing a connection the broker:

To change the prefetch size for all consumer types you would use a connection URI similar to:

tcp://localhost:61616?jms.prefetchPolicy.all=50

To change the prefetch size for just queue consumer types you would use a connection URI similar to:

tcp://localhost:61616?jms.prefetchPolicy.queuePrefetch=1

It can also be configured on a per consumer basis using Destination Options.

queue = new ActiveMQQueue("TEST.QUEUE?consumer.prefetchSize=10");
consumer = session.createConsumer(queue);

Pooled Connections and prefetch

Consuming messages from a connection pool can be problematic due to prefetch. Unconsumed prefetched messages are only released when a connection is closed, but with a pooled connection the connection close is deferred (for reuse) till the connection pool closes. This leaves prefetched messages unconsumed till the connection is reused. This feature can present as missing or out-of-sequence messages when there is more than one connection in the pool.
One solution is to use pooled connections for producers and a non-pooled connection for consumers. This might have performance impacts on the consumer side, if multiple threads try to consume messages at a fast rate. Alternatively, reduce the pool size to 1 for consumers. A third alternative is to reduce the prefetchSize to 1 or 0 with the pooled connection factory. When using Spring JMS and MessageDrivenPojo, you cannot use a prefetch of 0, so use 1 instead.

Ram versus Performance tradeoff

Setting a relatively high value of prefetch leads to higher performance; so the default values are typically > 1000; usually higher for topics and higher for the non-persistent messaging. The prefetch size dictates how many messages will be held in RAM on the client so if your RAM is limited you may want to set a low value such as 1 or 10 etc.

Graphic Design By Hiram