This section covers ways to optimize MRG Messaging applications to improve performance. Some optimizations involve the structure of your code, others involve tuning parameters.
Benchmarks can be utilized to determine the expected throughput and latency of MRG Messaging in your environment. Red Hat supplies a set of benchmark applications in the
qpidc-perftest package. This package contains:
Each of these programs provide options for testing performance in different conditions. The can be viewed by using the
--help option at the shell prompt:
$ perftest --help
$ latencytest --help
You can use the benchmarking tools to determine how changing a setting affects performance in your environment, so you can find the best settings to use in your program.
If these benchmarks perform significantly better than your application using the same configuration and settings, it is quite likely that MRG Messaging is not the bottleneck.
Note
This section is still under development. If you have found any tricks for optimizing your installation, why not let us know, and get it included in this document? Just follow the instructions in
Reporting a Bug.
Try these first
When you start optimizing your installation of MRG Messaging, try these things first:
Use asynchronous communication with the broker where possible. The Qpid APIs allow both synchronous and asynchronous communication. When performing a large amount of message transfers, asynchronous communication is much faster.
- From C++
The Session interface will issue commands synchronously. The AsyncSession interface will issue them asynchronously. You can convert from one to the other easily using async(session) and sync(session). You can also synchronise at a specific point using session.sync()
- From Python
session.auto_sync = False will turn off the default synchronous behaviour. You can synchronise explicitly using session.sync(timeout)
- From Java JMS
All messaging is asynchronous in the Java JMS client, however persistent messaging offers both synchronous and asynchronous publishing. Synchronous messaging publishing is more reliable, but can be quite slow.
Synchronous publishing can be set by adding the -Dsync_persistence=true option as a global property. This will make all publishing slower.
Synchronous publishing can also be set as a connection option. More information on doing this is available from the MRG Messaging Tutorial
Accept messages in batches rather than one by one. This decreases network traffic, while still guaranteeing delivery.
- From C++
If you are using automatic acceptance, set the
autoAck setting in
SubscriptionSettings to a value greater than
1 (the value is the size of the batch that will be accepted). If managing accepts manually, do so in batches. For example:
//a batch of messages is identified by a SequenceSet containing
//the relevant Ids:
qpid::framing::SequenceSet batch;
//add message ids to the set:
batch.add(message.getId());
//etc
subscription.accept(batch);
//or
session.messageAccept(batch);
- From Python
batch = RangedSet() ...
#add message ids to the set
batch.add(message.id)
#etc
session.message_accept(batch)
- From Java JMS
Check the MRG Messaging Tutorial for instructions.
For exclusive, auto-delete queues there is often no real value to using the explicit accept mode. Turning off the need to accept messages at all may also offer a performance gain. In C++, this is achieved by specifying
ACCEPT_MODE_NONE as the
acceptMode in
SubscriptionSettings. In Python you would specify the accept mode when issuing a subscription request:
session.messageSubscribe(queue='q', accept_mode=session.accept_mode.none). In JMS the queues used for topic subscriptions will do this automatically.
Use pre-fetch. Pre-fetching instructs the broker to deliver messages to the client in anticipation of them being consumed.
- From C++
Pre-fetch is controlled through the
flowControl setting on
SubscriptionSettings. The default is an unlimited pre-fetch which may overwhelm the client. For example, to set a prefetch of 100 messages:
SubscriptionSettings settings;
settings.flowControl = FlowControl::messageWindow(100);
- From Python
The default is an unlimited pre-fetch. For example, to reduce that to a prefetch window of 10 messages:
session.message_subscribe(destination="my-subscriber", queue="my-queue") session.message_set_flow_mode(destination="my-subscriber", session.flow_mode.window) session.message_flow(destination="my-subscriber", session.credit_unit.message, 100) session.message_flow(destination="my-subscriber", session.credit_unit.byte, 0xFFFFFFFF)
- From Java JMS
Use either the DUPS_OK or AUTO_ACK acknowledgement mode. For compliance with the JMS specification, the AUTO_ACK acknowledgement mode should always be used with a pre-fetch value of 0. This ensures one message is received and acknowledged at a time, which results in slow performance.
The maximum pre-fetch amount can be set by adding the -Dsync_prefetch=800 option as a global property. The default value is 1000.
The maximum pre-fetch amount can also be set as a connection option. More information on doing this is available from the MRG Messaging Tutorial
Consider enabling
TCP-NODELAY. This will generally improve latency but can also impact throughput. However if you are using very small transactions with a synchronous commit, this option can also improve throughput.
- From C++
Set the
tcpNodelay option to
true on the
ConnectionSettings instance passed to
Connection::open().
#include <qpid/client/ConnectionSettings.h>
ConnectionSettings connectionSettings;
connectionSettings.host = "localhost";
connectionSettings.port = 5672;
connectionSettings.tcpNoDelay = true;
connection.open(connectionSettings);
Consider tuning the maximum frame size used. This will affect the degree to which the broker tries to batch messages for delivery to clients. To improve latency, try reducing the value from the default 64kb. This will not prevent messages larger than the max frame size being sent, but it will impact the maximum size of the message headers. Picking a value that is large enough for the majority of messages to fit in a single content frame is likely to be most optimal.
- From C++
Set the
maxFrameSize option on the
ConnectionSettings instance passed to
Connection::open().
#include <qpid/client/ConnectionSettings.h>
ConnectionSettings connectionSettings;
connectionSettings.host = "localhost";
connectionSettings.port = 5672;
connectionSettings.maxFrameSize = 65535;
connection.open(connectionSettings);
- From Java JMS
This option is not configurable under Java JMS
Consider using bounds to control the size of the outgoing message queue. This specifies the maximum number of buffers that the outgoing message queue can hold.
- From C++
Set the
bounds property on the
ConnectionSettings instance passed to
Connection::open().
#include <qpid/client/ConnectionSettings.h>
ConnectionSettings connectionSettings;
connectionSettings.host = "localhost";
connectionSettings.port = 5672;
connectionSettings.maxFrameSize = 65535;
connectionSettings.bounds = 4;
connection.open(connectionSettings);
Experiment with different options using the perftest tool, available from the qpidc-perftest package.