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 "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>
|
||
<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>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>
|
||
<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>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>
|
||
<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>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>
|
||
<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>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>
|
||
<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>ActiveMQConnectionFactory.setProducerMaxRate(int
|
||
consumerMaxRate)</literal> method.</para>
|
||
</section>
|
||
</section>
|
||
</section>
|
||
</chapter>
|