activemq-artemis/docs/user-manual/en/perf-tuning.xml

306 lines
20 KiB
XML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?xml version="1.0" encoding="UTF-8"?>
<!-- ============================================================================= -->
<!-- Copyright © 2009 Red Hat, Inc. and others. -->
<!-- -->
<!-- The text of and illustrations in this document are licensed by Red Hat under -->
<!-- a Creative Commons AttributionShare Alike 3.0 Unported license ("CC-BY-SA"). -->
<!-- -->
<!-- An explanation of CC-BY-SA is available at -->
<!-- -->
<!-- http://creativecommons.org/licenses/by-sa/3.0/. -->
<!-- -->
<!-- In accordance with CC-BY-SA, if you distribute this document or an adaptation -->
<!-- of it, you must provide the URL for the original version. -->
<!-- -->
<!-- Red Hat, as the licensor of this document, waives the right to enforce, -->
<!-- and agrees not to assert, Section 4d of CC-BY-SA to the fullest extent -->
<!-- permitted by applicable law. -->
<!-- ============================================================================= -->
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
<!ENTITY % BOOK_ENTITIES SYSTEM "HornetQ_User_Manual.ent">
%BOOK_ENTITIES;
]>
<chapter id="perf-tuning">
<title>Performance Tuning</title>
<para>In this chapter we'll discuss how to tune HornetQ for optimum performance.</para>
<section>
<title>Tuning persistence</title>
<itemizedlist>
<listitem>
<para>Put the message journal on its own physical volume. If the disk is shared with
other processes e.g. transaction co-ordinator, database or other journals which
are also reading and writing from it, then this may greatly reduce performance
since the disk head may be skipping all over the place between the different
files. One of the advantages of an append only journal is that disk head
movement is minimised - this advantage is destroyed if the disk is shared. If
you're using paging or large messages make sure they're ideally put on separate
volumes too.</para>
</listitem>
<listitem>
<para>Minimum number of journal files. Set <literal>journal-min-files</literal> to a
number of files that would fit your average sustainable rate. If you see new
files being created on the journal data directory too often, i.e. lots of data
is being persisted, you need to increase the minimal number of files, this way
the journal would reuse more files instead of creating new data files.</para>
</listitem>
<listitem>
<para>Journal file size. The journal file size should be aligned to the capacity of
a cylinder on the disk. The default value 10MiB should be enough on most
systems.</para>
</listitem>
<listitem>
<para>Use AIO journal. If using Linux, try to keep your journal type as AIO. AIO
will scale better than Java NIO.</para>
</listitem>
<listitem>
<para>Tune <literal>journal-buffer-timeout</literal>. The timeout can be increased
to increase throughput at the expense of latency.</para>
</listitem>
<listitem>
<para>If you're running AIO you might be able to get some better performance by
increasing <literal>journal-max-io</literal>. DO NOT change this parameter if
you are running NIO.</para>
</listitem>
</itemizedlist>
</section>
<section>
<title>Tuning JMS</title>
<para>There are a few areas where some tweaks can be done if you are using the JMS
API</para>
<itemizedlist>
<listitem>
<para>Disable message id. Use the <literal>setDisableMessageID()</literal> method on
the <literal>MessageProducer</literal> class to disable message ids if you don't
need them. This decreases the size of the message and also avoids the overhead
of creating a unique ID.</para>
</listitem>
<listitem>
<para>Disable message timestamp. Use the <literal
>setDisableMessageTimeStamp()</literal> method on the <literal
>MessageProducer</literal> class to disable message timestamps if you don't
need them.</para>
</listitem>
<listitem>
<para>Avoid <literal>ObjectMessage</literal>. <literal>ObjectMessage</literal> is
convenient but it comes at a cost. The body of a <literal
>ObjectMessage</literal> uses Java serialization to serialize it to bytes.
The Java serialized form of even small objects is very verbose so takes up a lot
of space on the wire, also Java serialization is slow compared to custom
marshalling techniques. Only use <literal>ObjectMessage</literal> if you really
can't use one of the other message types, i.e. if you really don't know the type
of the payload until run-time.</para>
</listitem>
<listitem>
<para>Avoid <literal>AUTO_ACKNOWLEDGE</literal>. <literal>AUTO_ACKNOWLEDGE</literal>
mode requires an acknowledgement to be sent from the server for each message
received on the client, this means more traffic on the network. If you can, use
<literal>DUPS_OK_ACKNOWLEDGE</literal> or use <literal
>CLIENT_ACKNOWLEDGE</literal> or a transacted session and batch up many
acknowledgements with one acknowledge/commit. </para>
</listitem>
<listitem>
<para>Avoid durable messages. By default JMS messages are durable. If you don't
really need durable messages then set them to be non-durable. Durable messages
incur a lot more overhead in persisting them to storage.</para>
</listitem>
<listitem>
<para>Batch many sends or acknowledgements in a single transaction. HornetQ will
only require a network round trip on the commit, not on every send or
acknowledgement.</para>
</listitem>
</itemizedlist>
</section>
<section>
<title>Other Tunings</title>
<para>There are various other places in HornetQ where we can perform some tuning:</para>
<itemizedlist>
<listitem>
<para>Use Asynchronous Send Acknowledgements. If you need to send durable messages
non transactionally and you need a guarantee that they have reached the server
by the time the call to send() returns, don't set durable messages to be sent
blocking, instead use asynchronous send acknowledgements to get your
acknowledgements of send back in a separate stream, see <xref
linkend="send-guarantees"/> for more information on this.</para>
</listitem>
<listitem>
<para>Use pre-acknowledge mode. With pre-acknowledge mode, messages are acknowledged
<literal>before</literal> they are sent to the client. This reduces the
amount of acknowledgement traffic on the wire. For more information on this, see
<xref linkend="pre-acknowledge"/>.</para>
</listitem>
<listitem>
<para>Disable security. You may get a small performance boost by disabling security
by setting the <literal>security-enabled</literal> parameter to <literal
>false</literal> in <literal>hornetq-configuration.xml</literal>.</para>
</listitem>
<listitem>
<para>Disable persistence. If you don't need message persistence, turn it off
altogether by setting <literal>persistence-enabled</literal> to false in
<literal>hornetq-configuration.xml</literal>.</para>
</listitem>
<listitem>
<para>Sync transactions lazily. Setting <literal
>journal-sync-transactional</literal> to <literal>false</literal> in
<literal>hornetq-configuration.xml</literal> can give you better
transactional persistent performance at the expense of some possibility of loss
of transactions on failure. See <xref linkend="send-guarantees"/> for more
information.</para>
</listitem>
<listitem>
<para>Sync non transactional lazily. Setting <literal
>journal-sync-non-transactional</literal> to <literal>false</literal> in
<literal>hornetq-configuration.xml</literal> can give you better
non-transactional persistent performance at the expense of some possibility of
loss of durable messages on failure. See <xref linkend="send-guarantees"/> for
more information.</para>
</listitem>
<listitem>
<para>Send messages non blocking. Setting <literal>block-on-durable-send</literal>
and <literal>block-on-non-durable-send</literal> to <literal>false</literal> in
<literal>hornetq-jms.xml</literal> (if you're using JMS and JNDI) or
directly on the ServerLocator. This means you don't have to wait a whole
network round trip for every message sent. See <xref linkend="send-guarantees"/>
for more information.</para>
</listitem>
<listitem>
<para>If you have very fast consumers, you can increase consumer-window-size. This
effectively disables consumer flow control.</para>
</listitem>
<listitem>
<para>Socket NIO vs Socket Old IO. By default HornetQ uses old (blocking) on the
server and the client side (see the chapter on configuring transports for more
information <xref linkend="configuring-transports"/>). NIO is much more scalable
but can give you some latency hit compared to old blocking IO. If you need to be
able to service many thousands of connections on the server, then you should
make sure you're using NIO on the server. However, if don't expect many
thousands of connections on the server you can keep the server acceptors using
old IO, and might get a small performance advantage.</para>
</listitem>
<listitem>
<para>Use the core API not JMS. Using the JMS API you will have slightly lower
performance than using the core API, since all JMS operations need to be
translated into core operations before the server can handle them. If using the
core API try to use methods that take <literal>SimpleString</literal> as much as
possible. <literal>SimpleString</literal>, unlike java.lang.String does not
require copying before it is written to the wire, so if you re-use <literal
>SimpleString</literal> instances between calls then you can avoid some
unnecessary copying.</para>
</listitem>
</itemizedlist>
</section>
<section>
<title>Tuning Transport Settings</title>
<itemizedlist>
<listitem>
<para>TCP buffer sizes. If you have a fast network and fast machines you may get a
performance boost by increasing the TCP send and receive buffer sizes. See the
<xref linkend="configuring-transports"/> for more information on this. </para>
<note>
<para> Note that some operating systems like later versions of Linux include TCP
auto-tuning and setting TCP buffer sizes manually can prevent auto-tune from
working and actually give you worse performance!</para>
</note>
</listitem>
<listitem>
<para>Increase limit on file handles on the server. If you expect a lot of
concurrent connections on your servers, or if clients are rapidly opening and
closing connections, you should make sure the user running the server has
permission to create sufficient file handles.</para>
<para>This varies from operating system to operating system. On Linux systems you
can increase the number of allowable open file handles in the file <literal
>/etc/security/limits.conf</literal> e.g. add the lines
<programlisting>
serveruser soft nofile 20000
serveruser hard nofile 20000</programlisting>
This would allow up to 20000 file handles to be open by the user <literal
>serveruser</literal>. </para>
</listitem>
<listitem>
<para>Use <literal>batch-delay</literal> and set <literal>direct-deliver</literal>
to false for the best throughput for very small messages. HornetQ comes with a
preconfigured connector/acceptor pair (<literal>netty-throughput</literal>) in
<literal>hornetq-configuration.xml</literal> and JMS connection factory
(<literal>ThroughputConnectionFactory</literal>) in <literal
>hornetq-jms.xml</literal>which can be used to give the very best
throughput, especially for small messages. See the <xref
linkend="configuring-transports"/> for more information on this.</para>
</listitem>
</itemizedlist>
</section>
<section>
<title>Tuning the VM</title>
<para>We highly recommend you use the latest Java JVM for the best performance. We test
internally using the Sun JVM, so some of these tunings won't apply to JDKs from other
providers (e.g. IBM or JRockit)</para>
<itemizedlist>
<listitem>
<para>Garbage collection. For smooth server operation we recommend using a parallel
garbage collection algorithm, e.g. using the JVM argument <literal
>-XX:+UseParallelOldGC</literal> on Sun JDKs.</para>
</listitem>
<listitem id="perf-tuning.memory">
<para>Memory settings. Give as much memory as you can to the server. HornetQ can run
in low memory by using paging (described in <xref linkend="paging"/>) but if it
can run with all queues in RAM this will improve performance. The amount of
memory you require will depend on the size and number of your queues and the
size and number of your messages. Use the JVM arguments <literal>-Xms</literal>
and <literal>-Xmx</literal> to set server available RAM. We recommend setting
them to the same high value.</para>
</listitem>
<listitem>
<para>Aggressive options. Different JVMs provide different sets of JVM tuning
parameters, for the Sun Hotspot JVM the full list of options is available <ulink
url="http://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html"
>here</ulink>. We recommend at least using <literal
>-XX:+AggressiveOpts</literal> and<literal>
-XX:+UseFastAccessorMethods</literal>. You may get some mileage with the
other tuning parameters depending on your OS platform and application usage
patterns.</para>
</listitem>
</itemizedlist>
</section>
<section>
<title>Avoiding Anti-Patterns</title>
<itemizedlist>
<listitem>
<para>Re-use connections / sessions / consumers / producers. Probably the most
common messaging anti-pattern we see is users who create a new
connection/session/producer for every message they send or every message they
consume. This is a poor use of resources. These objects take time to create and
may involve several network round trips. Always re-use them.</para>
<note>
<para>Some popular libraries such as the Spring JMS Template are known to use
these anti-patterns. If you're using Spring JMS Template and you're getting
poor performance you know why. Don't blame HornetQ! The Spring JMS Template
can only safely be used in an app server which caches JMS sessions (e.g.
using JCA), and only then for sending messages. It cannot be safely be used
for synchronously consuming messages, even in an app server. </para>
</note>
</listitem>
<listitem>
<para>Avoid fat messages. Verbose formats such as XML take up a lot of space on the
wire and performance will suffer as result. Avoid XML in message bodies if you
can.</para>
</listitem>
<listitem>
<para>Don't create temporary queues for each request. This common anti-pattern
involves the temporary queue request-response pattern. With the temporary queue
request-response pattern a message is sent to a target and a reply-to header is
set with the address of a local temporary queue. When the recipient receives the
message they process it then send back a response to the address specified in
the reply-to. A common mistake made with this pattern is to create a new
temporary queue on each message sent. This will drastically reduce performance.
Instead the temporary queue should be re-used for many requests.</para>
</listitem>
<listitem>
<para>Don't use Message-Driven Beans for the sake of it. As soon as you start using
MDBs you are greatly increasing the codepath for each message received compared
to a straightforward message consumer, since a lot of extra application server
code is executed. Ask yourself do you really need MDBs? Can you accomplish the
same task using just a normal message consumer?</para>
</listitem>
</itemizedlist>
</section>
</chapter>