AMQP 1.0

Detailed documentation on the AMQP 1.0 pubsub component, used with brokers such as Apache ActiveMQ Artemis and Solace PubSub+

This component speaks AMQP 1.0. It is verified against Apache ActiveMQ Artemis and Solace PubSub+.

Component format

To set up AMQP pub/sub, create a component of type pubsub.amqp. See the pub/sub broker component file to learn how ConsumerID is automatically generated. Read the How-to: Publish and Subscribe guide on how to create and apply a pub/sub configuration.

apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: amqp-pubsub
spec:
  type: pubsub.amqp
  version: v1
  metadata:
    - name: url
      value: 'amqp://localhost:5672'
    - name: username
      value: 'default'
    - name: password
      value: 'default'

Spec metadata fields

FieldRequiredDetailsExample
urlYAddress of the AMQP broker. Can be secretKeyRef to use a secret reference.
Use the amqp:// URI scheme for non-TLS communication.
Use the amqps:// URI scheme for TLS communication.
"amqp://host.domain[:port]"
usernameNThe username to connect to the broker. Required unless anonymous is true.default
passwordNThe password to connect to the broker. Required unless anonymous is true.default
anonymousNSkip SASL username/password authentication (SASL ANONYMOUS). Combine with clientCert and clientKey over amqps:// to authenticate with the client certificate only (mutual TLS). Must be allowed on the broker.true
caCertNCertificate Authority (CA) certificate in PEM format for verifying server TLS certificates. Only needed when the broker certificate is not signed by a publicly trusted CA. Requires the amqps:// scheme."-----BEGIN CERTIFICATE-----\n<base64-encoded DER>\n-----END CERTIFICATE-----"
clientCertNTLS client certificate in PEM format, for mutual TLS. Must be used with clientKey, and requires the amqps:// scheme."-----BEGIN CERTIFICATE-----\n<base64-encoded DER>\n-----END CERTIFICATE-----"
clientKeyNTLS client key in PEM format, for mutual TLS. Must be used with clientCert, and requires the amqps:// scheme. Can be secretKeyRef to use a secret reference."-----BEGIN RSA PRIVATE KEY-----\n<base64-encoded PKCS8>\n-----END RSA PRIVATE KEY-----"
topicAddressPrefixNPrefix prepended to the AMQP address of a topic, used for a bare topic name and for topics named topic:<name>. Defaults to no prefix, which addresses the topic by name. See Addressing topics and queues.multicast://
queueAddressPrefixNPrefix prepended to the AMQP address of a queue, used for topics named queue:<name>. Defaults to no prefix, which addresses the queue by name. See Addressing topics and queues.anycast://
containerIDNAMQP container ID of the connections that carry named subscriptions. The broker identifies a named subscription by the container ID and the subscription name. Defaults to the consumer ID that Dapr gives every pub/sub component: the app ID, or consumerID if the component sets it (see the pub/sub broker component file). So by default every replica of the app shares it. Do not set containerID or consumerID to a per-replica value, such as the {podName} or {uuid} markers: each replica then gets a subscription of its own and a copy of every message. See Named, durable and shared subscriptions.orders-service
durableMessagesNMark every published message as durable, so that the broker keeps it across a broker restart. AMQP 1.0 messages are not durable unless explicitly specified. Defaults to false.true
applicationPropertiesFromMetadataNCopy the metadata of each publish request into the message, so that a broker-side filter can match on it. Defaults to false. See Filter on message properties.true
backOffPolicyNRetry policy used between attempts to re-open a subscription after the broker connection drops. Either constant or exponential. Defaults to constant."constant"
backOffDurationNDelay between attempts to re-open a subscription, when backOffPolicy is constant. Defaults to 5s. The component retries for as long as the subscription exists, so this sets the pace, not a limit.1s
backOffInitialIntervalNFirst delay between attempts, when backOffPolicy is exponential. Defaults to 500ms.100ms
backOffMaxIntervalNLongest delay between attempts, when backOffPolicy is exponential. Defaults to 60s.30s

Communication using TLS

To configure communication using TLS:

  1. Ensure that the broker is configured to support certificates. For mutual TLS on ActiveMQ Artemis, set sslEnabled=true;needClientAuth=true on the acceptor so clients without a trusted certificate are rejected.
  2. Provide the caCert, clientCert, and clientKey metadata in the component configuration.

For example:

apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: amqp-pubsub
spec:
  type: pubsub.amqp
  version: v1
  metadata:
  - name: url
    value: "amqps://host.domain[:port]"
  - name: username
    value: 'default'
  - name: password
    value: 'default'
  - name: caCert
    value: ${{ myLoadedCACert }}
  - name: clientCert
    value: ${{ myLoadedClientCert }}
  - name: clientKey
    secretKeyRef:
      name: myAmqpClientKey
      key: myAmqpClientKey
auth:
  secretStore: <SECRET_STORE_NAME>

While the caCert and clientCert values may not be secrets, they can be referenced from a Dapr secret store as well for convenience.

To authenticate with the client certificate only (mutual TLS), set anonymous to true and omit username and password:

apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: amqp-pubsub
spec:
  type: pubsub.amqp
  version: v1
  metadata:
  - name: url
    value: "amqps://host.domain[:port]"
  - name: anonymous
    value: "true"
  - name: caCert
    value: ${{ myLoadedCACert }}
  - name: clientCert
    value: ${{ myLoadedClientCert }}
  - name: clientKey
    secretKeyRef:
      name: myAmqpClientKey
      key: myAmqpClientKey
auth:
  secretStore: <SECRET_STORE_NAME>

Before Dapr 1.19, anonymous was ignored over amqps:// and SASL PLAIN was always used.

Addressing topics and queues

The component turns a Dapr topic name into an AMQP address. topicAddressPrefix and queueAddressPrefix control that translation, and both default to no prefix. So out of the box the AMQP address is the Dapr topic name, unchanged:

Dapr topic nameAMQP address, with the default prefixes
ordersorders
topic:ordersorders
queue:ordersorders

A topic name that already begins with one of the configured prefixes is used as the AMQP address unchanged.

Brokers that namespace destinations

Some brokers select the routing type from a prefix on the address. Set topicAddressPrefix and queueAddressPrefix to the prefixes that broker uses, and the queue: and topic: schemes then select the routing type.

Apache ActiveMQ Artemis

An Artemis acceptor takes anycastPrefix and multicastPrefix settings. If the acceptor is configured with anycastPrefix=anycast://;multicastPrefix=multicast://, configure the component to match. Use whatever prefixes your acceptor defines; queue:// and topic:// are also common:

apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: artemis
spec:
  type: pubsub.amqp
  version: v1
  metadata:
    - name: url
      value: 'amqp://localhost:5672'
    - name: username
      value: 'artemis'
    - name: password
      value: 'artemis'
    - name: topicAddressPrefix
      value: 'multicast://'
    - name: queueAddressPrefix
      value: 'anycast://'

A Dapr topic named orders is then addressed as multicast://orders, and queue:orders as anycast://orders.

Without these prefixes, the routing type comes from the broker instead, through the default-address-routing-type of the matching address setting.

Consume from a queue that the broker manages

Artemis accepts a fully qualified queue name, <address>::<queue>. Subscribe to it to consume from a queue defined in broker.xml, with the durability and filter configured there:

apiVersion: dapr.io/v2alpha1
kind: Subscription
metadata:
  name: billing
spec:
  pubsubname: artemis
  topic: "orders::billing"
  routes:
    default: /billing

Every replica of the app consumes from the same queue, and closing the subscription never deletes the queue. Create the queue before the app starts: a queue that Artemis creates automatically is subject to auto-delete-queues.

This example uses the default address prefixes, which are empty. If you set topicAddressPrefix, the component adds it in front of the topic name, so the link opens on <prefix>orders::billing.

Solace PubSub+

Solace addresses topics as topic://<name> and queues as queue://<name>:

    - name: topicAddressPrefix
      value: 'topic://'
    - name: queueAddressPrefix
      value: 'queue://'

Named, durable and shared subscriptions

By default, each subscription attaches with the broker defaults. On a multicast address that is usually a temporary subscription: each replica of the app gets its own copy of every message, and messages published while the app is down are lost.

A subscription can set the keys below in its own metadata. They use standard AMQP 1.0 mechanisms, so they apply to any broker that supports durable subscriptions, the shared capability and the JMS selector filter. They are verified with ActiveMQ Artemis. See Named subscriptions on ActiveMQ Artemis for how Artemis applies them, and Other brokers.

FieldDetailsExample
subscriptionNameName of the subscription, sent as the AMQP link name. Together with containerID, it identifies the subscription on the broker. Required for durable and shared. Must not contain |, and must be unique among the subscriptions of one component. Not allowed on a queue (queue:<name>), which has no subscription to name.billing
durableAsk the broker to keep the subscription, and the messages it holds, while no consumer is attached. The component sends a durable source that never expires. Defaults to false.true
sharedAsk for a shared subscription, with the shared source capability. Every replica that uses the same subscriptionName then consumes from one subscription, so that each message reaches one replica. Defaults to false.true
selectorA JMS selector, sent as the apache.org:selector-filter:string filter, that the broker applies to the subscription. Messages that do not match never enter it. If the broker attaches without the filter, the subscription fails. Can be set without subscriptionName.eventType = 'invoice'

For example:

apiVersion: dapr.io/v2alpha1
kind: Subscription
metadata:
  name: billing
spec:
  pubsubname: artemis
  topic: orders
  routes:
    default: /billing
  metadata:
    subscriptionName: billing
    durable: "true"
    shared: "true"
    selector: "eventType = 'invoice'"

Every replica of the app consumes from one durable subscription named billing, which holds only the messages that match the selector.

Keep these points in mind, on any broker:

  • Replicas. Set shared when the app has more than one replica. A broker can let only one consumer attach to a durable subscription that is not shared.
  • Several subscriptions on one topic. Dapr keeps one declarative or programmatic subscription per pub/sub component and topic. To hold several named subscriptions on one topic, define one component per subscription, as in the example below, or use streaming subscriptions.
  • Changing a selector. A broker can treat a changed selector as a new subscription, and drop the messages the old one held. To change a selector, use a new subscriptionName.
  • Removing a subscription. Dapr never deletes a durable subscription, even when the subscription is removed from the app. Delete it on the broker.
  • Broker restarts. A durable subscription keeps durable messages only. Set durableMessages on the publishing component, or publish durable messages from other clients. JMS clients publish durable messages by default.
  • Permissions. The broker user needs permission to create durable subscriptions and to consume from them. With anonymous: "true" there is no user name: give the permissions to the role that the broker maps the connection to, for example the role of the client certificate.
  • Connections. Each named subscription uses a connection of its own, so that the container ID stays fixed and stopping the subscription never deletes it.

Several subscriptions on one topic

Dapr maps one declarative or programmatic subscription per pub/sub component and topic. Thus, two named subscriptions on the topic orders, each with its own selector, require two Dapr components on the same broker:

apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: billing
spec:
  type: pubsub.amqp
  version: v1
  metadata:
    - name: url
      value: 'amqp://localhost:5672'
    - name: username
      value: 'artemis'
    - name: password
      value: 'artemis'
---
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: audit
spec:
  type: pubsub.amqp
  version: v1
  metadata:
    - name: url
      value: 'amqp://localhost:5672'
    - name: username
      value: 'artemis'
    - name: password
      value: 'artemis'

Each component is then referenced by a single subscription:

apiVersion: dapr.io/v2alpha1
kind: Subscription
metadata:
  name: billing
spec:
  pubsubname: billing
  topic: orders
  routes:
    default: /billing
  metadata:
    subscriptionName: billing
    durable: "true"
    shared: "true"
    selector: "eventType = 'invoice'"
---
apiVersion: dapr.io/v2alpha1
kind: Subscription
metadata:
  name: audit
spec:
  pubsubname: audit
  topic: orders
  routes:
    default: /audit
  metadata:
    subscriptionName: audit
    durable: "true"
    shared: "true"
    selector: "eventType = 'login'"

Both components use the same container ID, the app ID by default, and the subscription names keep the two subscriptions apart.

Named subscriptions on ActiveMQ Artemis

On ActiveMQ Artemis, the keys behave as follows:

  • Queue names. Artemis names the queue of a named subscription <containerID>.<subscriptionName>. For example, with the app ID orders-service, the billing subscription above is the durable queue orders-service.billing on the address orders, with the selector as its filter.
  • Without the keys. Each subscription gets a temporary queue with a generated name, which Artemis removes when the subscription detaches.
  • Replicas. A durable queue that is not shared accepts one consumer. Artemis refuses the others.
  • Changing a selector. Artemis compares the selector each time a consumer attaches. If the selector changed and no consumer is attached, Artemis recreates the queue and drops the messages it held. If consumers are attached, Artemis refuses the new consumer.
  • Removing a subscription. Delete the queue, for example from the Artemis console.
  • Selectors. A selector reads application properties by name, the AMQP subject as JMSType, and header fields such as AMQPriority and AMQDurable. Artemis does not translate JMS header names such as JMSPriority in these selectors. Application properties that Dapr copies are strings, so to compare one with a number, prefix the selector with convert_string_expressions:, for example convert_string_expressions:priority > 5.
  • Permissions. The role needs the createDurableQueue and consume permissions on the address.

To consume from a queue that Artemis manages instead, see Consume from a queue that the broker manages.

Other brokers

These keys are not verified with other brokers, Solace PubSub+ included. A broker that does not support the shared capability can refuse a second replica or give it a copy of every message. If a broker ignores the selector filter, the component fails the subscription, because the broker does not return the filter.

Filter on message properties

A broker-side filter, such as a selector, matches on message properties. By default, a message that Dapr publishes carries no application properties. Set applicationPropertiesFromMetadata to true on the publishing component to copy the metadata of each publish request into the message:

  • The subject key sets the AMQP subject.
  • Every other key becomes an application property with a string value.
  • ttlInSeconds, rawPayload and cloudevent.* keys are not copied.

These are standard AMQP 1.0 message properties, so any broker or client can read them. The selector syntax depends on the broker. For ActiveMQ Artemis, see Named subscriptions on ActiveMQ Artemis.

The mapping works in one direction. The component does not pass the application properties of a received message to the app.

For example, this publish request reaches the billing subscription above:

curl -X POST "http://localhost:3500/v1.0/publish/artemis/orders?metadata.eventType=invoice" \
  -H "Content-Type: application/json" \
  -d '{"orderId": "100"}'

When a subscriber returns an error

A message whose handler returns an error is returned to the broker as modified with delivery-failed. The broker redelivers it and counts the attempt, so the broker’s own redelivery-delay and max-delivery-attempts settings apply and a message that always fails eventually reaches the dead-letter address.

Redelivery pacing and the attempt limit are broker settings, not component settings. On ActiveMQ Artemis they live in address-settings; make sure a dead-letter address and max-delivery-attempts are configured there.

Migrating from pubsub.solace.amqp

pubsub.amqp applies no address prefix by default, where pubsub.solace.amqp applies the Solace convention. To migrate a Solace component without changing the addresses it uses, change the type and set both prefixes explicitly:

spec:
  type: pubsub.amqp          # was pubsub.solace.amqp
  version: v1
  metadata:
    - name: topicAddressPrefix
      value: 'topic://'
    - name: queueAddressPrefix
      value: 'queue://'

Everything else in the component stays the same.

Create a broker

Run an Artemis broker locally using Docker:

docker run -d -p 5672:5672 -p 8161:8161 --env ARTEMIS_USER=artemis --env ARTEMIS_PASSWORD=artemis --name artemis apache/activemq-artemis:latest

The AMQP acceptor listens on amqp://localhost:5672, and the web console on http://localhost:8161.

Run a Solace broker locally using Docker:

docker run -d -p 8080:8080 -p 55554:55555 -p 8008:8008 -p 1883:1883 -p 8000:8000 -p 5672:5672 -p 9000:9000 -p 2222:2222 --shm-size=2g --env username_admin_globalaccesslevel=admin --env username_admin_password=admin --name=solace solace/solace-pubsub-standard

You can then interact with the server using the client port: amqp://localhost:5672

You can also sign up for a free SaaS broker on Solace Cloud.