213 lines
8.8 KiB
Markdown
213 lines
8.8 KiB
Markdown
# AMQP
|
|
|
|
Apache ActiveMQ Artemis supports the [AMQP
|
|
1.0](https://www.oasis-open.org/committees/tc_home.php?wg_abbrev=amqp)
|
|
specification. By default there are `acceptor` elements configured to accept
|
|
AMQP connections on ports `61616` and `5672`.
|
|
|
|
See the general [Protocols and Interoperability](protocols-interoperability.md)
|
|
chapter for details on configuring an `acceptor` for AMQP.
|
|
|
|
You can use *any* AMQP 1.0 compatible clients.
|
|
|
|
A short list includes:
|
|
|
|
- [qpid clients](https://qpid.apache.org/download.html)
|
|
- [.NET Clients](https://blogs.apache.org/activemq/entry/using-net-libraries-with-activemq)
|
|
- [Javascript NodeJS](https://github.com/noodlefrenzy/node-amqp10)
|
|
- [Java Script RHEA](https://github.com/grs/rhea)
|
|
- ... and many others.
|
|
|
|
## Examples
|
|
|
|
We have a few examples as part of the Artemis distribution:
|
|
|
|
- .NET:
|
|
- ./examples/protocols/amqp/dotnet
|
|
- ProtonCPP
|
|
- ./examples/protocols/amqp/proton-cpp
|
|
- ./examples/protocols/amqp/proton-clustered-cpp
|
|
- Ruby
|
|
- ./examples/protocols/amqp/proton-ruby
|
|
- Java (Using the qpid JMS Client)
|
|
- ./examples/protocols/amqp/queue
|
|
- Interceptors
|
|
- ./examples/features/standard/interceptor-amqp
|
|
- ./examples/features/standard/broker-plugin
|
|
|
|
## Message Conversions
|
|
|
|
The broker will not perform any message conversion to any other protocols when
|
|
sending AMQP and receiving AMQP.
|
|
|
|
However if you intend your message to be received by an AMQP JMS Client, you
|
|
must follow the [JMS Mapping
|
|
Conventions](https://www.oasis-open.org/committees/download.php/53086/amqp-bindmap-jms-v1.0-wd05.pdf).
|
|
If you send a body type that is not recognized by this specification the
|
|
conversion between AMQP and any other protocol will make it a Binary Message.
|
|
Make sure you follow these conventions if you intend to cross protocols or
|
|
languages. Especially on the message body.
|
|
|
|
A compatibility setting allows aligning the naming convention of AMQP queues
|
|
(JMS Durable and Shared Subscriptions) with CORE. For backwards compatibility
|
|
reasons, you need to explicitly enable this via broker configuration:
|
|
|
|
- `amqp-use-core-subscription-naming`
|
|
- `true` - use queue naming convention that is aligned with CORE.
|
|
- `false` (default) - use older naming convention.
|
|
|
|
## Intercepting and changing messages
|
|
|
|
We don't recommend changing messages at the server's side for a few reasons:
|
|
|
|
- AMQP messages are meant to be immutable
|
|
- The message won't be the original message the user sent
|
|
- AMQP has the possibility of signing messages. The signature would be broken.
|
|
- For performance reasons. We try not to re-encode (or even decode) messages.
|
|
|
|
If regardless these recommendations you still need and want to intercept and
|
|
change AMQP messages, look at the aforementioned interceptor examples.
|
|
|
|
## AMQP and security
|
|
|
|
The Apache ActiveMQ Artemis Server accepts the PLAIN, ANONYMOUS, and GSSAPI
|
|
SASL mechanism. These are implemented on the broker's [security](security.md)
|
|
infrastructure.
|
|
|
|
## AMQP and destinations
|
|
|
|
If an AMQP Link is dynamic then a temporary queue will be created and either
|
|
the remote source or remote target address will be set to the name of the
|
|
temporary queue. If the Link is not dynamic then the address of the remote
|
|
target or source will be used for the queue. In case it does not exist,
|
|
it will be auto-created if the settings allow.
|
|
|
|
## AMQP and Multicast Addresses (Topics)
|
|
|
|
Although AMQP has no notion of "topics" it is still possible to treat AMQP
|
|
consumers or receivers as subscriptions rather than just consumers on a queue.
|
|
By default any receiving link that attaches to an address that has only
|
|
`multicast` enabled will be treated as a subscription and a corresponding
|
|
subscription queue will be created. If the Terminus Durability is either
|
|
`UNSETTLED_STATE` or `CONFIGURATION` then the queue will be made durable
|
|
(similar to a JMS durable subscription) and given a name made up from the
|
|
container id and the link name, something like `my-container-id:my-link-name`.
|
|
If the Terminus Durability is configured as `NONE` then a volatile `multicast`
|
|
queue will be created.
|
|
|
|
## AMQP and Coordinations - Handling Transactions
|
|
|
|
An AMQP links target can also be a Coordinator. A Coordinator is used to handle
|
|
transactions. If a coordinator is used then the underlying server session will
|
|
be transacted and will be either rolled back or committed via the coordinator.
|
|
|
|
> **Note:**
|
|
>
|
|
> AMQP allows the use of multiple transactions per session,
|
|
> `amqp:multi-txns-per-ssn`, however in this version of Apache ActiveMQ Artemis
|
|
> will only support single transactions per session.
|
|
|
|
## AMQP scheduling message delivery
|
|
|
|
An AMQP message can provide scheduling information that controls the time in
|
|
the future when the message will be delivered at the earliest. This
|
|
information is provided by adding a message annotation to the sent message.
|
|
|
|
There are two different message annotations that can be used to schedule a
|
|
message for later delivery:
|
|
|
|
- `x-opt-delivery-time`
|
|
The specified value must be a positive long corresponding to the time the
|
|
message should be made available for delivery (in milliseconds).
|
|
|
|
- `x-opt-delivery-delay`
|
|
The specified value must be a positive long corresponding to the amount of
|
|
milliseconds after the broker receives the given message before it should be
|
|
made available for delivery.
|
|
|
|
If both annotations are present in the same message then the broker will prefer
|
|
the more specific `x-opt-delivery-time` value.
|
|
|
|
## DLQ and Expiry transfer
|
|
|
|
AMQP Messages will be copied before transferred to a DLQ or ExpiryQueue and will receive properties and annotations during this process.
|
|
|
|
The broker also keeps an internal only property (called extra property) that is not exposed to the clients, and those will also be filled during this process.
|
|
|
|
Here is a list of Annotations and Property names AMQP Messages will receive when transferred:
|
|
|
|
|Annotation name| Internal Property Name|Description|
|
|
|---------------|-----------------------|-----------|
|
|
|x-opt-ORIG-MESSAGE-ID|_AMQ_ORIG_MESSAGE_ID|The original message ID before the transfer|
|
|
|x-opt-ACTUAL-EXPIRY|_AMQ_ACTUAL_EXPIRY|When the expiry took place. Milliseconds since epoch times|
|
|
|x-opt-ORIG-QUEUE|_AMQ_ORIG_QUEUE|The original queue name before the transfer|
|
|
|x-opt-ORIG-ADDRESS|_AMQ_ORIG_ADDRESS|The original address name before the transfer|
|
|
|
|
## Filtering on Message Annotations
|
|
|
|
It is possible to filter on messaging annotations if you use the prefix "m." before the annotation name.
|
|
|
|
For example if you want to filter messages sent to a specific destination, you could create your filter accordingly to this:
|
|
|
|
```java
|
|
ConnectionFactory factory = new JmsConnectionFactory("amqp://localhost:5672");
|
|
Connection connection = factory.createConnection();
|
|
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
|
|
connection.start();
|
|
javax.jms.Queue queue = session.createQueue("my-DLQ");
|
|
MessageConsumer consumer = session.createConsumer(queue, "\"m.x-opt-ORIG-ADDRESS\"='ORIGINAL_PLACE'");
|
|
Message message = consumer.receive();
|
|
```
|
|
|
|
The broker will set internal properties. If you intend to filter after DLQ or Expiry you may choose the internal property names:
|
|
|
|
```java
|
|
// Replace the consumer creation on the previous example:
|
|
MessageConsumer consumer = session.createConsumer(queue, "_AMQ_ORIG_ADDRESS='ORIGINAL_PLACE'");
|
|
```
|
|
|
|
|
|
## Configuring AMQP Idle Timeout
|
|
|
|
It is possible to configure the AMQP Server's IDLE Timeout by setting the property amqpIdleTimeout in milliseconds on the acceptor.
|
|
|
|
This will make the server to send an AMQP frame open to the client, with your configured timeout / 2.
|
|
|
|
So, if you configured your AMQP Idle Timeout to be 60000, the server will tell the client to send frames every 30,000 milliseconds.
|
|
|
|
|
|
```xml
|
|
<acceptor name="amqp">.... ;amqpIdleTimeout=<configured-timeout>; ..... </acceptor>
|
|
```
|
|
|
|
|
|
### Disabling Keep alive checks
|
|
|
|
if you set amqpIdleTimeout=0 that will tell clients to not sending keep alive packets towards the server. On this case
|
|
you will rely on TCP to determine when the socket needs to be closed.
|
|
|
|
```xml
|
|
<acceptor name="amqp">.... ;amqpIdleTimeout=0; ..... </acceptor>
|
|
```
|
|
|
|
This contains a real example for configuring amqpIdleTimeout:
|
|
|
|
```xml
|
|
<acceptor name="amqp">tcp://0.0.0.0:5672?amqpIdleTimeout=0;tcpSendBufferSize=1048576;tcpReceiveBufferSize=1048576;protocols=AMQP;useEpoll=true;amqpCredits=1000;amqpLowCredits=300;directDeliver=false;batchDelay=10</acceptor>
|
|
```
|
|
|
|
## Web Sockets
|
|
|
|
Apache ActiveMQ Artemis also supports AMQP over [Web
|
|
Sockets](https://html.spec.whatwg.org/multipage/web-sockets.html). Modern web
|
|
browsers which support Web Sockets can send and receive AMQP messages.
|
|
|
|
AMQP over Web Sockets is supported via a normal AMQP acceptor:
|
|
|
|
```xml
|
|
<acceptor name="amqp-ws-acceptor">tcp://localhost:5672?protocols=AMQP</acceptor>
|
|
```
|
|
|
|
With this configuration, Apache ActiveMQ Artemis will accept AMQP connections
|
|
over Web Sockets on the port `5672`. Web browsers can then connect to
|
|
`ws://<server>:5672` using a Web Socket to send and receive AMQP messages. |