Memory limits, when configured, prevent the broker from running out of resources. The default behavior, 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 behavior 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.
Figure 1.5 gives an overview of what happens to a messaging application when flow control is 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 behavior 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 vmCursor on Destination for more details about cursors). |
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 1.6.
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.
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.
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>
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
—for non-persistent messages, specifies the maximum amount of memory used to hold the messages.storeUsage
—for persistent messages, specifies the maximum disk storage for the messages.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"/>
When a producer is subject to flow control, the default behavior 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 behavior.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>