activemq-artemis/docs/user-manual/en/flow-control.xml

317 lines
19 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 "ActiveMQ_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, ActiveMQ 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, ActiveMQ 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, ActiveMQ 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 ActiveMQ 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>activemq-jms.xml</literal>:</para>
<programlisting>
&lt;connection-factory name="ConnectionFactory">
&lt;connectors>
&lt;connector-ref connector-name="netty-connector"/>
&lt;/connectors>
&lt;entries>
&lt;entry name="ConnectionFactory"/>
&lt;/entries>
&lt;!-- Set the consumer window size to 0 to have *no* buffer on the client side -->
&lt;consumer-window-size>0&lt;/consumer-window-size>
&lt;/connection-factory></programlisting>
<para>If the connection factory is directly instantiated, the consumer window size is
specified by <literal>ActiveMQConnectionFactory.setConsumerWindowSize()</literal>
method.</para>
<para>Please see <xref linkend="examples.no-consumer-buffering"/> for an example which
shows how to configure ActiveMQ 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 ActiveMQ 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>activemq-jms.xml</literal>:</para>
<programlisting>
&lt;connection-factory name="ConnectionFactory">
&lt;connectors>
&lt;connector-ref connector-name="netty-connector"/>
&lt;/connectors>
&lt;entries>
&lt;entry name="ConnectionFactory"/>
&lt;/entries>
&lt;!-- We limit consumers created on this connection factory to consume messages at a maximum rate
of 10 messages per sec -->
&lt;consumer-max-rate>10&lt;/consumer-max-rate>
&lt;/connection-factory></programlisting>
<para>If the connection factory is directly instantiated, the max rate size can be set
via the <literal>ActiveMQConnectionFactory.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 ActiveMQ to prevent consumer buffering when dealing with slow
consumers.</para>
</section>
</section>
</section>
<section>
<title>Producer flow control</title>
<para>ActiveMQ 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, ActiveMQ 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 ActiveMQ 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>activemq-jms.xml</literal>:</para>
<programlisting>
&lt;connection-factory name="ConnectionFactory">
&lt;connectors>
&lt;connector-ref connector-name="netty-connector"/>
&lt;/connectors>
&lt;entries>
&lt;entry name="ConnectionFactory"/>
&lt;/entries>
&lt;producer-window-size>10&lt;/producer-window-size>
&lt;/connection-factory></programlisting>
<para>If the connection factory is directly instantiated, the producer window size can
be set via the <literal>ActiveMQConnectionFactory.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>
&lt;address-settings>
&lt;address-setting match="jms.queue.exampleQueue">
&lt;max-size-bytes>100000&lt;/max-size-bytes>
&lt;address-full-policy>BLOCK&lt;/address-full-policy>
&lt;/address-setting>
&lt;/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>ActiveMQ also allows the rate a producer can emit message to be limited, in units of
messages per second. By specifying such a rate, ActiveMQ 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 ActiveMQ 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>activemq-jms.xml</literal>:</para>
<programlisting>
&lt;connection-factory name="ConnectionFactory">
&lt;connectors>
&lt;connector-ref connector-name="netty-connector"/>
&lt;/connectors>
&lt;entries>
&lt;entry name="ConnectionFactory"/>
&lt;/entries>
&lt;!-- We limit producers created on this connection factory to produce messages at a maximum rate
of 10 messages per sec -->
&lt;producer-max-rate>10&lt;/producer-max-rate>
&lt;/connection-factory></programlisting>
<para>If the connection factory is directly instantiated, the max rate size can be set
via the <literal>ActiveMQConnectionFactory.setProducerMaxRate(int
consumerMaxRate)</literal> method.</para>
</section>
</section>
</section>
</chapter>