4.2. Flow Control
Overview
Memory limits, when configured, prevent the broker from running out of resources. The default behaviour, when a limit is reached, is to block the sending thread in the broker, which blocks the destination and the connection.
Producer flow control is a mechanism that pushes the blocking behaviour onto the client, so that the producer thread blocks if the broker has no space. With producer flow control, the producer has a send window that is dependent on broker memory. When the send window is full, it blocks on the client.
Flow control enabled
Figure 4.1, “Broker with Flow Control Enabled” gives an overview of what happens to a messaging application when flow control is enabled.
Figure 4.1. Broker with Flow Control Enabled
If a consumer is very slow at acknowledging messages (or stops acknowledging messages altogether), the broker continues to dispatch messages to the consumer until it reaches the prefetch limit, after which the messages start to back up on the broker. Assuming the producer continues to produce lots of messages and the consumer continues to be very slow, the broker will start to run short of memory resources as it holds on to pending messages for the consumer.
When the consumed memory resources start to approach their limit (as defined either by the per-destination memory limit or the per-broker memory limit), the flow control mechanism activates automatically in order to protect the broker resources. The broker sends a message to the producer asking it either to slow down or to stop sending messages to the broker. This protects the broker from running out of memory (and other) resources.
Note
There are some differences in behaviour between a persistent broker and a non-persistent broker. If a broker is persistent, pending messages are stored to disk, but flow control can still be triggered if the amount of memory used by a cursor approaches its limit (see Section 6.3, “vmCursor on Destination” for more details about cursors).
Flow control disabled
While it is generally a good idea to enable flow control in a broker, there are some scenarios for which it is unsuitable. Consider the scenario where a producer dispatches messages that are consumed by multiple consumers (for example, consuming from a topic), but one of the consumers could fail without the broker becoming aware of it right away. This scenario is shown in Figure 4.2, “Broker with Flow Control Disabled”.
Figure 4.2. Broker with Flow Control Disabled
Because the slow consumer remains blocked for a very long time (possibly indefinitely), after flow control kicks in, the producer also ceases producing messages for a very long time (possibly indefinitely). This is an undesirable outcome, because there are other active consumers interested in the messages coming from the producer and they are now being unfairly deprived of those messages.
In this case, it is better to turn off flow control in the broker, so that the producer can continue sending messages to the other interested consumers. The broker now resorts to an alternative strategy to avoid running out of memory: the broker writes any pending messages for the slow consumer to a temporary file. Ultimately, this scenario is resolved in one of two ways: either the slow consumer revives again and consumes all of the messages from the temporary file; or the broker determines that the slow consumer has died and the temporary file can be discarded.
Discarding messages
By default, when flow control is disabled and the relevant memory limit is reached, the slow consumer's messages are backed up in a temporary file. An alternative strategy for coping with the excess messages, however, is simply to discard the slow consumer's messages when they exceed a certain limit (where the oldest messages are discarded first). This strategy avoids the overhead of writing to a temporary file.
For example, if the slow consumer is receiving a feed of real-time stock quotes, it might be acceptable to discard older, undelivered stock quotes, because the information becomes stale.
To enable discarding of messages, define a pending message limit strategy in the broker configuration. For example, to specify that the backlog of messages stored in the broker (not including the prefetched messages) cannot exceed 10 for any topics that match the
PRICES.>
pattern (that is, topic names prefixed by PRICES.
), configure the broker as follows:
<beans ... > <broker ...> <!-- lets define the dispatch policy --> <destinationPolicy> <policyMap> <policyEntries> <policyEntry topic="PRICES.>"> <!-- lets force old messages to be discarded for slow consumers --> <pendingMessageLimitStrategy> <constantPendingMessageLimitStrategy limit="10"/> </pendingMessageLimitStrategy> </policyEntry> ... </policyEntries> </policyMap> </destinationPolicy> </broker> </beans>
For more details about how to configure pending message limit strategies, see http://activemq.apache.org/slow-consumer-handling.html.
How to turn off flow control
Flow control can be turned off by setting a destination policy in the broker's configuration. In particular, flow control can be enabled or disabled on individual destinations or groups of destinations (using wildcards). To disable flow control, set the
producerFlowControl
attribute to false
on a policyEntry
element.
For example, to configure a broker to disable flow control for all topic destinations starting with
FOO.
, insert a policy entry like the following into the broker's configuration:
<broker ... > ... <destinationPolicy> <policyMap> <policyEntries> <policyEntry topic="FOO.>" producerFlowControl="false"/> ... </policyEntries> </policyMap> </destinationPolicy> ... </broker>
Defining the memory limits
When flow control is enabled, the point at which flow control activates depends on the defined memory limits, which can be specified at either of the following levels of granularity:
- Per-broker—to set global memory limits on a broker, define a
systemUsage
element as a child of thebroker
element, as follows:<broker> ... <systemUsage> <systemUsage> <memoryUsage> <memoryUsage limit="64 mb" /> </memoryUsage> <storeUsage> <storeUsage limit="100 gb" /> </storeUsage> <tempUsage> <tempUsage limit="10 gb" /> </tempUsage> </systemUsage> </systemUsage> ... </broker>
Where the preceding sample specifies three distinct memory limits, as follows:memoryUsage
—specifies the maximum amount of memory allocated to the broker.storeUsage
—for persistent messages, specifies the maximum disk storage for the messages.NoteIn certain scenarios, the actual disk storage used by JBoss A-MQ can exceed the specified limit. For this reason, it is recommended that you setstoreUsage
to about 70% of the intended maximum disk storage.tempUsage
—for temporary messages, specifies the maximum amount of memory.
The values shown in the preceding example are the defaults. - Per-destination—to set a memory limit on a destination, set the
memoryLimit
attribute on thepolicyEntry
element. The value ofmemoryLimit
can be a string, such as10 MB
or512 KB
. For example, to limit the amount of memory on theFOO.BAR
queue to 10 MB, define a policy entry like the following:<policyEntry queue="FOO.BAR" memoryLimit="10 MB"/>
Making a producer aware of flow control
When a producer is subject to flow control, the default behaviour is for the
send()
operation to block, until enough memory is freed up in the broker for the producer to resume sending messages. If you want the producer to be made aware of the fact that the send()
operation is blocked due to flow control, you can enable either of the following attributes on the systemUsage
element:
sendFailIfNoSpace
- If
true
, the broker immediately returns an error when flow control is preventing producersend()
operations; otherwise, revert to default behaviour. sendFailIfNoSpaceAfterTimeout
- Specifies a timeout in units of milliseconds. When flow control is preventing producer
send()
operations, the broker returns an error, after the specified timeout has elapsed.
The following example shows how to configure the broker to return an error to the producer immediately, whenever flow control is blocking the producer
send()
operations:
<broker>
...
<systemUsage>
<systemUsage sendFailIfNoSpace="true">
<memoryUsage>
<memoryUsage limit="64 mb" />
</memoryUsage>
...
</systemUsage>
</systemUsage>
...
</broker>