activemq-artemis/docs/user-manual/en/address-model.md

18 KiB
Raw Blame History

Address Model

Every messaging protocol and API that Apache ActiveMQ Artemis supports defines a different set of messaging resources.

  • JMS uses queues and topics
  • STOMP uses generic destinations
  • MQTT uses topics
  • AMQP uses generic nodes

In order to deal the the unique semantics and use-cases for each of these the broker has a flexible and powerful address model based on the following core set of resources:

  • address
  • queue
  • routing type

Address

Messages are sent to an address. An address is given a unique name, a routing type, and zero or more queues.

Queue

Messages are consumed from a queue. A queue is bound to an address. It is given a unique name and a routing type. There can be zero or more queues bound to one address. When a message is sent to an address it is routed to one or more of its queues based on the configured routing type.

The name of the queue must be globally unique. For example, you can't have a queue named q1 on address a1 and also a queue named q1 address a2.

Routing Type

A routing type determines how messages are routed from an address to the queue(s) bound to that address. Two different routing types are supported, anycast and multicast.

If you want your messages routed to... Use this routing type...
a single anycast queue on the address. anycast
every multicast queue on the address. multicast

Note:

It is possible to define queues with a different routing type for the same address, but this typically results in an anti-pattern and is therefore not recommended.

Automatic Configuration

By default Apache ActiveMQ Artemis will automatically create addresses and queues to support the semantics of whatever protocol you're using. The broker understands how to support each protocol's functionality with the core resources so that in most cases no manual configuration is required. This saves you from having to preconfigure each address and queue before a client can connect to it.

The broker can optionally be configured to automatically delete addresses and queues when they are no longer in use.

Automatic creation and deletion is configured on a per address basis and is controlled by the following address-setting elements:

  • auto-create-addresses
  • auto-delete-addresses
  • default-address-routing-type
  • auto-create-queues
  • auto-delete-queues
  • default-queue-routing-type

See the documentation on address settings for more details on these elements.

Of course, automatic configuration can be disabled and everything can be configured manually. Read on for more details about manual configuration.

Basic Manual Configuration

The following examples show how to configure resources for basic anycast and multicast use-cases.

Note:

Many of the details of these use-cases are protocol agnostic. The goal here is to demonstrate and explain the basic configuration elements and how the address model works fundamentally.

Anycast

The most common use-case for anycast semantics, sometimes referred to as point-to-point, involves applications following a "competing consumer" pattern to receive messages from a shared queue. The more consumers receiving messages the greater the overall message throughput. Multiple Java applications sharing a JMS queue is a classic example of this use-case.

In this use-case the broker is configured, for example, with an address, address.foo using the anycast routing type with just one queue, q1. When a producer sends a message to address.foo it is then routed to q1 and finally dispatched to one of the consumers.

Anycast Figure 1. Anycast

This is what the configuration for this use-case would look like in etc/broker.xml:

<addresses>
   <address name="address.foo">
      <anycast>
         <queue name="q1"/>
      </anycast>
   </address>
</addresses>

For most protocols and APIs which support this kind of use-case (e.g. JMS, AMQP, etc.) it is customary to use the same name when sending and consuming messages. In that case you'd use a configuration like this:

<addresses>
   <address name="orderQueue">
      <anycast>
         <queue name="orderQueue"/>
      </anycast>
   </address>
</addresses>

Multicast

The most common use-case for multicast semantics, sometimes referred to as publish/subscribe or "pub/sub", involves each application receiving every message sent to an address. Multiple applications consuming from a JMS topic is a classic example of this use-case. MQTT subscriptions is another supported example of multicast semantics.

In this use-case the broker is configured with an address, address.foo using the multicast routing type with two queues, q1 & q2. When a producer sends a message to address.foo it is then routed to both q1 & q2 so that ultimately both consumers receive the same messages.

Multicast Figure 2. Multicast

This is what the configuration for this use-case would look like in etc/broker.xml:

<addresses>
   <address name="address.foo">
      <multicast>
         <queue name="q1"/>
         <queue name="q2"/>
      </multicast>
   </address>
</addresses>

This basic configuration is simple and straight-forward, but there's a problem. In a normal pub/sub use-case like with a JMS topic or with MQTT the number of subscribers isn't known ahead of time. In that case, this is the recommended configuration:

<addresses>
   <address name="address.foo">
      <multicast/>
   </address>
</addresses>

Define <multicast/> with no queues and the broker will automatically create queues for each subscription when the consumers connect to address.foo. Then when a message is sent to address.foo it will be routed to each queue for each subscriber and therefore each subscriber will get every message. These queues are often referred to as subscription queues for obvious reasons.

These subscription queues are typically named based on the semantics of the protocol used to create them. For example, JMS supports durable and non-durable subscriptions. The queue for a non-durable subscription is named with a UUID, but the queue used for a durable subscription is named according to the JMS "client ID" and "subscription name." Similar conventions are used for AMQP, MQTT, STOMP, etc.

Advanced Manual Configuration

Fully Qualified Queue Names

In most cases its not necessary or desirable to statically configure the aforementioned subscription queues. However, there are scenarios where a user may want to statically configure a subscription queue and later connect to that queue directly using a Fully Qualified Queue Name (FQQN).

An FQQN uses a special syntax to specify both the address and the queue so that applications using protocols and APIs which don't natively understand the address/queue separation (e.g. AMQP, JMS, etc.) can send messages or subscribe directly to a queue rather than being limited to the address. Applications simply need to use the address name and the queue name separated by :: (e.g. address::queue).

In this example, the address a1 is configured with two queues: q1, q2 as shown in the configuration below.

<addresses>
   <address name="a1">
      <multicast>
         <queue name="q1" />
         <queue name="q2" />
      </multicast>
   </address>
</addresses>

Here's a snippet of Java code using JMS which demonstrates the FQQN syntax:

Queue q1 session.createQueue("a1::q1");
MessageConsumer consumer = session.createConsumer(q1);

Note

The string :: should only be used for FQQN and not in any other context in address or queue names.

The examples below show how to use broker side configuration to statically configure a queue with publish subscribe behavior for shared, non-shared, durable and non-durable subscription behavior.

Shared, Durable Subscription Queue using max-consumers

The default behavior for queues is to not limit the number connected queue consumers. The max-consumers parameter of the queue element can be used to limit the number of connected consumers allowed at any one time.

Open the file etc/broker.xml for editing.

<addresses>
   <address name="durable.foo">
      <multicast>
         <!-- pre-configured shared durable subscription queue -->
         <queue name="q1" max-consumers="10">
            <durable>true</durable>
         </queue>
      </multicast>
   </address>
</addresses>

Non-shared, Durable Subscription Queue

The broker can be configured to prevent more than one consumer from connecting to a queue at any one time. The subscriptions to queues configured this way are therefore "non-shared". To do this simply set the max-consumers parameter to 1:

<addresses>
   <address name="durable.foo">
      <multicast>
         <!-- pre-configured non shared durable subscription queue -->
         <queue name="q1" max-consumers="1">
            <durable>true</durable>
         </queue>
      </multicast>
   </address>
</addresses>

Non-durable Subscription Queue

Non-durable subscriptions are again usually managed by the relevant protocol manager, by creating and deleting temporary queues.

If a user requires to pre-create a queue that behaves like a non-durable subscription queue the purge-on-no-consumers flag can be enabled on the queue. When purge-on-no-consumers is set to true. The queue will not start receiving messages until a consumer is attached. When the last consumer is detached from the queue. The queue is purged (its messages are removed) and will not receive any more messages until a new consumer is attached.

Open the file etc/broker.xml for editing.

<addresses>
   <address name="non.shared.durable.foo">
      <multicast>
         <queue name="orders1" purge-on-no-consumers="true"/>
      </multicast>
   </address>
</addresses>

Disabled Queue

If a user requires to statically configure a queue and disable routing to it, for example where a queue needs to be defined so a consumer can bind, but you want to disable message routing to it for the time being.

Or you need to stop message flow to the queue to allow investigation keeping the consumer bound, but don't wish to have further messages routed to the queue to avoid message build up.

When enabled is set to true the queue will have messages routed to it. (default)

When enabled is set to false the queue will NOT have messages routed to it.

Open the file etc/broker.xml for editing.

<addresses>
   <address name="foo.bar">
      <multicast>
         <queue name="orders1" enabled="false"/>
      </multicast>
   </address>
</addresses>

Warning:

Disabling all the queues on an address means that any message sent to that address will be silently dropped.

Temporary Queues

For some protocols and APIs which only support monolithic "destinations" without the address/queue separation (e.g. AMQP, JMS, etc.) temporary queues are created by the broker using a UUID (i.e universally unique identifier) as the name for both the address and the queue. Because the name is a UUID it is impossible to create an address-setting for it whose match is anything but #.

To solve this problem one can specify the temporary-queue-namespace in broker.xml and then create an address-setting whose match value corresponds to the configured temporary-queue-namespace. When the temporary-queue-namespace is set and a temporary queue is created then the broker will prepend the temporary-queue-namespace value along with the delimiter value configured in wildcard-addresses (defaults to .) to the address name and use that to lookup the associated address-setting values.

Here's a simple example configuration:

<temporary-queue-namespace>temp</temporary-queue-namespace>

<address-settings>
   <address-setting match="temp.#">
      <enable-metrics>false</enable-metrics>
   </address-setting>
</address-settings>

Using this configuration any temporary queue will have metrics disabled.

Note:

This setting does not change the actual name of the temporary queue. It only changes the name used to lookup the address-settings.

Other Advanced Configurations

Each of the following advanced configurations have their own chapter so their details are not repeated here:

How to filter messages

Apache ActiveMQ Artemis supports the ability to filter messages using Filter Expressions.

Filters can be applied in two places - on a queue and on a consumer.

Filtering messages on a queue increases performance vs. filtering on the consumer because the messages don't need to be scanned. However, a queue filter is often not as flexible.

Queue Filter

When a filter is applied to a queue, messages are filtered before they are routed to the queue. To add a filter use the filter element when configuring a queue, e.g.:

<addresses>
   <address name="filter">
      <anycast>
         <queue name="filter">
            <filter string="color='red'"/>
         </queue>
      </anycast>
   </address>
</addresses>

The filter defined above ensures that only messages with an attribute "color='red'" is sent to this queue.

Consumer Filters

Consumer filters are applied after messages have routed to the queue and are defined using the appropriate client APIs. The following JMS example shows how consumer filters work.

Define an address with a single queue, with no filter applied in etc/broker.xml.

<addresses>
   <address name="filter">
      <anycast>
         <queue name="filter"/>
      </anycast>
   </address>
</addresses>

Then send some messages to the queue.

...
// Send some messages
for (int i = 0; i < 3; i ++) {
  TextMessage redMessage = senderSession.createTextMessage("Red");
  redMessage.setStringProperty("color", "red");
  producer.send(redMessage)

  TextMessage greenMessage = senderSession.createTextMessage("Green");
  greenMessage.setStringProperty("color", "green");
  producer.send(greenMessage)
}

At this point the queue would have 6 messages: red, green, red, green, red, green.

Create a consumer with the filter color='red'.

MessageConsumer redConsumer = redSession.createConsumer(queue, "color='red'");

The redConsumer has a filter that only matches "red" messages. The redConsumer will receive 3 messages.

red, red, red

The resulting queue would now be

green, green, green

Alternate Ways to Determine Routing Type

Typically the routing type is determined either by the static XML configuration or by the default-address-routing-type and default-queue-routing-type address-setting elements used for automatic address and queue creation. However, there are two other ways to specify routing type:

  • a configurable prefix which client applications can use when sending messages or creating consumers
  • a property client applications can set on the messages they send

Using a Prefix to Determine Routing Type

These prefixes are configured using the anycastPrefix and multicastPrefix parameters within the URL of the acceptor which the client is using. When multiple values are needed, these can be separated by a comma.

Configuring an Anycast Prefix

In etc/broker.xml, add the anycastPrefix to the URL of the desired acceptor. In the example below, the acceptor is configured to use queue/ for the anycastPrefix. Client code can specify queue/foo/ if the client wants anycast routing.

<acceptor name="artemis">tcp://0.0.0.0:61616?protocols=AMQP;anycastPrefix=queue/</acceptor>

Consider, for example, a STOMP client that wants to send a message using anycast semantics to a queue that doesn't exist. Consider also that the broker is configured to auto-create addresses and queues, but the default-address-routing-type and default-queue-routing-type are both MULTICAST. Since the anycastPrefix is queue/ it can just send a message to queue/foo and the broker will automatically create an address named foo with an anycast queue also named foo.

Configuring a Multicast Prefix

In etc/broker.xml, add the multicastPrefix to the URL of the desired acceptor. In the example below, the acceptor is configured to use topic/ for the multicastPrefix. Client code can specify topic/foo/ if the client wants multicast routing.

<acceptor name="artemis">tcp://0.0.0.0:61616?protocols=AMQP;multicastPrefix=topic/</acceptor>

Consider, for example, a STOMP client that wants to create a subscription with multicast semantics on an address that doesn't exist. Consider also that the broker is configured to auto-create addresses and queues, but the default-address-routing-type and default-queue-routing-type are both ANYCAST. Since the multicastPrefix is topic/ it can just subscribe to topic/foo and the broker will automatically create an address named foo with a multicast queue for the subscription. Any messages sent to foo will then be routed to the subscription queue.

Using a Message Property to Determine Routing Type

The _AMQ_ROUTING_TYPE property represents a byte value which will be used by the broker to determine the routing type when a message is sent. Use 0 for anycast routing or 1 for multicast routing.

Note:

A message will only be routed to queues which match its _AMQ_ROUTING_TYPE property value (if any). For example, if a message with an _AMQ_ROUTING_TYPE value of 1 (i.e. multicast) is sent to an address that only has anycast queues then the message won't actually be routed to any of the queues since the routing types don't match. If no _AMQ_ROUTING_TYPE is set then the message will be routed to all the queues on the address according to the queues' routing semantics.

Configuring Addresses and Queues via Address Settings

This content has been relocated to its own chapter.