317 lines
19 KiB
XML
317 lines
19 KiB
XML
|
<?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 Attribution–Share 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="flow-control">
|
|||
|
<title>Flow Control</title>
|
|||
|
<para>Flow control is used to limit the flow of data between a client and server, or a server and
|
|||
|
another server in order to prevent the client or server being overwhelmed with data.</para>
|
|||
|
<section>
|
|||
|
<title>Consumer Flow Control</title>
|
|||
|
<para>This controls the flow of data between the server and the client as the client consumes
|
|||
|
messages. For performance reasons clients normally buffer messages before delivering to the
|
|||
|
consumer via the <literal>receive()</literal> method or asynchronously via a message
|
|||
|
listener. If the consumer cannot process messages as fast as they are being delivered and
|
|||
|
stored in the internal buffer, then you could end up with a situation where messages would
|
|||
|
keep building up possibly causing out of memory on the client if they cannot be processed
|
|||
|
in time.</para>
|
|||
|
<section id="flow-control.consumer.window">
|
|||
|
<title>Window-Based Flow Control</title>
|
|||
|
<para>By default, HornetQ consumers buffer messages from the server in a client side buffer
|
|||
|
before the client consumes them. This improves performance: otherwise every time the
|
|||
|
client consumes a message, HornetQ would have to go the server to request the next
|
|||
|
message. In turn, this message would then get sent to the client side, if one was
|
|||
|
available.</para>
|
|||
|
<para>A network round trip would be involved for <emphasis>every</emphasis> message and
|
|||
|
considerably reduce performance.</para>
|
|||
|
<para>To prevent this, HornetQ pre-fetches messages into a buffer on each consumer. The
|
|||
|
total maximum size of messages (in bytes) that will be buffered on each consumer is
|
|||
|
determined by the <literal>consumer-window-size</literal> parameter.</para>
|
|||
|
<para>By default, the <literal>consumer-window-size</literal> is set to 1 MiB (1024 * 1024
|
|||
|
bytes).</para>
|
|||
|
<para>The value can be:</para>
|
|||
|
<itemizedlist>
|
|||
|
<listitem>
|
|||
|
<para><literal>-1</literal> for an <emphasis>unbounded</emphasis> buffer</para>
|
|||
|
</listitem>
|
|||
|
<listitem>
|
|||
|
<para><literal>0</literal> to not buffer any messages. See <xref
|
|||
|
linkend="examples.no-consumer-buffering"/> for working example of a consumer
|
|||
|
with no buffering.</para>
|
|||
|
</listitem>
|
|||
|
<listitem>
|
|||
|
<para><literal>>0</literal> for a buffer with the given maximum size in
|
|||
|
bytes.</para>
|
|||
|
</listitem>
|
|||
|
</itemizedlist>
|
|||
|
<para>Setting the consumer window size can considerably improve performance depending on
|
|||
|
the messaging use case. As an example, let's consider the two extremes: </para>
|
|||
|
<variablelist>
|
|||
|
<varlistentry>
|
|||
|
<term>Fast consumers</term>
|
|||
|
<listitem>
|
|||
|
<para>Fast consumers can process messages as fast as they consume them (or even
|
|||
|
faster)</para>
|
|||
|
<para>To allow fast consumers, set the <literal>consumer-window-size</literal> to
|
|||
|
-1. This will allow <emphasis>unbounded</emphasis> message buffering on the
|
|||
|
client side.</para>
|
|||
|
<para>Use this setting with caution: it can overflow the client memory if the
|
|||
|
consumer is not able to process messages as fast as it receives them.</para>
|
|||
|
</listitem>
|
|||
|
</varlistentry>
|
|||
|
<varlistentry>
|
|||
|
<term>Slow consumers</term>
|
|||
|
<listitem>
|
|||
|
<para>Slow consumers takes significant time to process each message and it is
|
|||
|
desirable to prevent buffering messages on the client side so that they can be
|
|||
|
delivered to another consumer instead.</para>
|
|||
|
<para>Consider a situation where a queue has 2 consumers; 1 of which is very slow.
|
|||
|
Messages are delivered in a round robin fashion to both consumers, the fast
|
|||
|
consumer processes all of its messages very quickly until its buffer is empty.
|
|||
|
At this point there are still messages awaiting to be processed in the buffer
|
|||
|
of the slow consumer thus preventing them being processed by the fast consumer.
|
|||
|
The fast consumer is therefore sitting idle when it could be processing the
|
|||
|
other messages. </para>
|
|||
|
<para>To allow slow consumers, set the <literal>consumer-window-size</literal> to
|
|||
|
0 (for no buffer at all). This will prevent the slow consumer from buffering
|
|||
|
any messages on the client side. Messages will remain on the server side ready
|
|||
|
to be consumed by other consumers.</para>
|
|||
|
<para>Setting this to 0 can give deterministic distribution between multiple
|
|||
|
consumers on a queue.</para>
|
|||
|
</listitem>
|
|||
|
</varlistentry>
|
|||
|
</variablelist>
|
|||
|
<para>Most of the consumers cannot be clearly identified as fast or slow consumers but are
|
|||
|
in-between. In that case, setting the value of <literal>consumer-window-size</literal>
|
|||
|
to optimize performance depends on the messaging use case and requires benchmarks to
|
|||
|
find the optimal value, but a value of 1MiB is fine in most cases.</para>
|
|||
|
<section id="flow-control.core.api">
|
|||
|
<title>Using Core API</title>
|
|||
|
<para>If HornetQ Core API is used, the consumer window size is specified by <literal
|
|||
|
>ServerLocator.setConsumerWindowSize()</literal> method and some of the
|
|||
|
<literal>ClientSession.createConsumer()</literal> methods.</para>
|
|||
|
</section>
|
|||
|
<section>
|
|||
|
<title>Using JMS</title>
|
|||
|
<para>if JNDI is used to look up the connection factory, the consumer window size is
|
|||
|
configured in <literal>hornetq-jms.xml</literal>:</para>
|
|||
|
<programlisting>
|
|||
|
<connection-factory name="ConnectionFactory">
|
|||
|
<connectors>
|
|||
|
<connector-ref connector-name="netty-connector"/>
|
|||
|
</connectors>
|
|||
|
<entries>
|
|||
|
<entry name="ConnectionFactory"/>
|
|||
|
</entries>
|
|||
|
|
|||
|
<!-- Set the consumer window size to 0 to have *no* buffer on the client side -->
|
|||
|
<consumer-window-size>0</consumer-window-size>
|
|||
|
</connection-factory></programlisting>
|
|||
|
<para>If the connection factory is directly instantiated, the consumer window size is
|
|||
|
specified by <literal>HornetQConnectionFactory.setConsumerWindowSize()</literal>
|
|||
|
method.</para>
|
|||
|
<para>Please see <xref linkend="examples.no-consumer-buffering"/> for an example which
|
|||
|
shows how to configure HornetQ to prevent consumer buffering when dealing with slow
|
|||
|
consumers.</para>
|
|||
|
</section>
|
|||
|
</section>
|
|||
|
<section>
|
|||
|
<title>Rate limited flow control</title>
|
|||
|
<para>It is also possible to control the <emphasis>rate</emphasis> at which a consumer can
|
|||
|
consume messages. This is a form of throttling and can be used to make sure that a
|
|||
|
consumer never consumes messages at a rate faster than the rate specified. </para>
|
|||
|
<para>The rate must be a positive integer to enable this functionality and is the maximum
|
|||
|
desired message consumption rate specified in units of messages per second. Setting this
|
|||
|
to <literal>-1</literal> disables rate limited flow control. The default value is
|
|||
|
<literal>-1</literal>.</para>
|
|||
|
<para>Please see <xref linkend="examples.consumer-rate-limit"/> for a working example of
|
|||
|
limiting consumer rate.</para>
|
|||
|
<section id="flow-control.rate.core.api">
|
|||
|
<title>Using Core API</title>
|
|||
|
<para>If the HornetQ core API is being used the rate can be set via the <literal
|
|||
|
>ServerLocator.setConsumerMaxRate(int consumerMaxRate)</literal> method or
|
|||
|
alternatively via some of the <literal>ClientSession.createConsumer()</literal>
|
|||
|
methods. </para>
|
|||
|
</section>
|
|||
|
<section>
|
|||
|
<title>Using JMS</title>
|
|||
|
<para>If JNDI is used to look up the connection factory, the max rate can be configured
|
|||
|
in <literal>hornetq-jms.xml</literal>:</para>
|
|||
|
<programlisting>
|
|||
|
<connection-factory name="ConnectionFactory">
|
|||
|
<connectors>
|
|||
|
<connector-ref connector-name="netty-connector"/>
|
|||
|
</connectors>
|
|||
|
<entries>
|
|||
|
<entry name="ConnectionFactory"/>
|
|||
|
</entries>
|
|||
|
<!-- We limit consumers created on this connection factory to consume messages at a maximum rate
|
|||
|
of 10 messages per sec -->
|
|||
|
<consumer-max-rate>10</consumer-max-rate>
|
|||
|
</connection-factory></programlisting>
|
|||
|
<para>If the connection factory is directly instantiated, the max rate size can be set
|
|||
|
via the <literal>HornetQConnectionFactory.setConsumerMaxRate(int
|
|||
|
consumerMaxRate)</literal> method.</para>
|
|||
|
<note>
|
|||
|
<para>Rate limited flow control can be used in conjunction with window based flow
|
|||
|
control. Rate limited flow control only effects how many messages a client can
|
|||
|
consume in a second and not how many messages are in its buffer. So if you had a
|
|||
|
slow rate limit and a high window based limit the clients internal buffer would
|
|||
|
soon fill up with messages.</para>
|
|||
|
</note>
|
|||
|
<para>Please see <xref linkend="examples.consumer-rate-limit"/> for an example which
|
|||
|
shows how to configure HornetQ to prevent consumer buffering when dealing with slow
|
|||
|
consumers.</para>
|
|||
|
</section>
|
|||
|
</section>
|
|||
|
</section>
|
|||
|
<section>
|
|||
|
<title>Producer flow control</title>
|
|||
|
<para>HornetQ also can limit the amount of data sent from a client to a server to prevent the
|
|||
|
server being overwhelmed.</para>
|
|||
|
<section>
|
|||
|
<title>Window based flow control</title>
|
|||
|
<para>In a similar way to consumer window based flow control, HornetQ producers, by
|
|||
|
default, can only send messages to an address as long as they have sufficient credits to
|
|||
|
do so. The amount of credits required to send a message is given by the size of the
|
|||
|
message.</para>
|
|||
|
<para>As producers run low on credits they request more from the server, when the server
|
|||
|
sends them more credits they can send more messages.</para>
|
|||
|
<para>The amount of credits a producer requests in one go is known as the <emphasis
|
|||
|
role="italic">window size</emphasis>.</para>
|
|||
|
<para>The window size therefore determines the amount of bytes that can be in-flight at any
|
|||
|
one time before more need to be requested - this prevents the remoting connection from
|
|||
|
getting overloaded.</para>
|
|||
|
<section>
|
|||
|
<title>Using Core API</title>
|
|||
|
<para>If the HornetQ core API is being used, window size can be set via the <literal
|
|||
|
>ServerLocator.setProducerWindowSize(int producerWindowSize)</literal>
|
|||
|
method.</para>
|
|||
|
</section>
|
|||
|
<section>
|
|||
|
<title>Using JMS</title>
|
|||
|
<para>If JNDI is used to look up the connection factory, the producer window size can be
|
|||
|
configured in <literal>hornetq-jms.xml</literal>:</para>
|
|||
|
<programlisting>
|
|||
|
<connection-factory name="ConnectionFactory">
|
|||
|
<connectors>
|
|||
|
<connector-ref connector-name="netty-connector"/>
|
|||
|
</connectors>
|
|||
|
<entries>
|
|||
|
<entry name="ConnectionFactory"/>
|
|||
|
</entries>
|
|||
|
<producer-window-size>10</producer-window-size>
|
|||
|
</connection-factory></programlisting>
|
|||
|
<para>If the connection factory is directly instantiated, the producer window size can
|
|||
|
be set via the <literal>HornetQConnectionFactory.setProducerWindowSize(int
|
|||
|
producerWindowSize)</literal> method.</para>
|
|||
|
</section>
|
|||
|
<section>
|
|||
|
<title>Blocking producer window based flow control</title>
|
|||
|
<para>Normally the server will always give the same number of credits as have been
|
|||
|
requested. However, it is also possible to set a maximum size on any address, and the
|
|||
|
server will never send more credits than could cause the address's upper memory limit
|
|||
|
to be exceeded.</para>
|
|||
|
<para>For example, if I have a JMS queue called "myqueue", I could set the maximum
|
|||
|
memory size to 10MiB, and the the server will control the number of credits sent to
|
|||
|
any producers which are sending any messages to myqueue such that the total messages
|
|||
|
in the queue never exceeds 10MiB.</para>
|
|||
|
<para>When the address gets full, producers will block on the client side until more
|
|||
|
space frees up on the address, i.e. until messages are consumed from the queue thus
|
|||
|
freeing up space for more messages to be sent.</para>
|
|||
|
<para>We call this blocking producer flow control, and it's an efficient way to prevent
|
|||
|
the server running out of memory due to producers sending more messages than can be
|
|||
|
handled at any time.</para>
|
|||
|
<para>It is an alternative approach to paging, which does not block producers but
|
|||
|
instead pages messages to storage.</para>
|
|||
|
<para>To configure an address with a maximum size and tell the server that you want to
|
|||
|
block producers for this address if it becomes full, you need to define an
|
|||
|
AddressSettings (<xref linkend="queue-attributes.address-settings"/>) block for the
|
|||
|
address and specify <literal>max-size-bytes</literal> and <literal
|
|||
|
>address-full-policy</literal></para>
|
|||
|
<para>The address block applies to all queues registered to that address. I.e. the total
|
|||
|
memory for all queues bound to that address will not exceed <literal
|
|||
|
>max-size-bytes</literal>. In the case of JMS topics this means the <emphasis
|
|||
|
role="italic">total</emphasis> memory of all subscriptions in the topic won't
|
|||
|
exceed max-size-bytes.</para>
|
|||
|
<para>Here's an example:</para>
|
|||
|
<programlisting>
|
|||
|
<address-settings>
|
|||
|
<address-setting match="jms.queue.exampleQueue">
|
|||
|
<max-size-bytes>100000</max-size-bytes>
|
|||
|
<address-full-policy>BLOCK</address-full-policy>
|
|||
|
</address-setting>
|
|||
|
</address-settings></programlisting>
|
|||
|
<para>The above example would set the max size of the JMS queue "exampleQueue" to be
|
|||
|
100000 bytes and would block any producers sending to that address to prevent that
|
|||
|
max size being exceeded.</para>
|
|||
|
<para>Note the policy must be set to <literal>BLOCK</literal> to enable blocking producer
|
|||
|
flow control.</para>
|
|||
|
<note><para>Note that in the default configuration all addresses are set to block producers after 10 MiB of message data
|
|||
|
is in the address. This means you cannot send more than 10MiB of message data to an address without it being consumed before the producers
|
|||
|
will be blocked. If you do not want this behaviour increase the <literal>max-size-bytes</literal> parameter or change the
|
|||
|
address full message policy.</para>
|
|||
|
</note>
|
|||
|
</section>
|
|||
|
</section>
|
|||
|
<section>
|
|||
|
<title>Rate limited flow control</title>
|
|||
|
<para>HornetQ also allows the rate a producer can emit message to be limited, in units of
|
|||
|
messages per second. By specifying such a rate, HornetQ will ensure that producer never
|
|||
|
produces messages at a rate higher than that specified.</para>
|
|||
|
<para>The rate must be a positive integer to enable this functionality and is the maximum
|
|||
|
desired message consumption rate specified in units of messages per second. Setting this
|
|||
|
to <literal>-1</literal> disables rate limited flow control. The default value is
|
|||
|
<literal>-1</literal>.</para>
|
|||
|
<para>Please see the <xref linkend="producer-rate-limiting-example"/> for a working example
|
|||
|
of limiting producer rate.</para>
|
|||
|
<section id="flow-control.producer.rate.core.api">
|
|||
|
<title>Using Core API</title>
|
|||
|
<para>If the HornetQ core API is being used the rate can be set via the <literal
|
|||
|
>ServerLocator.setProducerMaxRate(int consumerMaxRate)</literal> method or
|
|||
|
alternatively via some of the <literal>ClientSession.createProducer()</literal>
|
|||
|
methods. </para>
|
|||
|
</section>
|
|||
|
<section>
|
|||
|
<title>Using JMS</title>
|
|||
|
<para>If JNDI is used to look up the connection factory, the max rate can be configured
|
|||
|
in <literal>hornetq-jms.xml</literal>:</para>
|
|||
|
<programlisting>
|
|||
|
<connection-factory name="ConnectionFactory">
|
|||
|
<connectors>
|
|||
|
<connector-ref connector-name="netty-connector"/>
|
|||
|
</connectors>
|
|||
|
<entries>
|
|||
|
<entry name="ConnectionFactory"/>
|
|||
|
</entries>
|
|||
|
<!-- We limit producers created on this connection factory to produce messages at a maximum rate
|
|||
|
of 10 messages per sec -->
|
|||
|
<producer-max-rate>10</producer-max-rate>
|
|||
|
</connection-factory></programlisting>
|
|||
|
<para>If the connection factory is directly instantiated, the max rate size can be set
|
|||
|
via the <literal>HornetQConnectionFactory.setProducerMaxRate(int
|
|||
|
consumerMaxRate)</literal> method.</para>
|
|||
|
</section>
|
|||
|
</section>
|
|||
|
</section>
|
|||
|
</chapter>
|