activemq-artemis/docs/user-manual/en/stomp.md

14 KiB

STOMP

STOMP is a text-orientated wire protocol that allows STOMP clients to communicate with STOMP Brokers. Apache ActiveMQ Artemis supports STOMP 1.0, 1.1 and 1.2.

STOMP clients are available for several languages and platforms making it a good choice for interoperability.

By default there are acceptor elements configured to accept STOMP connections on ports 61616 and 61613.

See the general Protocols and Interoperability chapter for details on configuring an acceptor for STOMP.

Refer to the STOMP examples for a look at some of this functionality in action.

Limitations

The STOMP specification identifies transactional acknowledgements as an optional feature. Support for transactional acknowledgements is not implemented in Apache ActiveMQ Artemis. The ACK frame can not be part of a transaction. It will be ignored if its transaction header is set.

Virtual Hosting

Apache ActiveMQ Artemis currently doesn't support virtual hosting, which means the host header in CONNECT frame will be ignored.

Mapping STOMP destinations to addresses and queues

STOMP clients deals with destinations when sending messages and subscribing. Destination names are simply strings which are mapped to some form of destination on the server - how the server translates these is left to the server implementation.

In Apache ActiveMQ Artemis, these destinations are mapped to addresses and queues depending on the operation being done and the desired semantics (e.g. anycast or multicast).

Logging

Incoming and outgoing STOMP frames can be logged by enabling DEBUG for org.apache.activemq.artemis.core.protocol.stomp.StompConnection. This can be extremely useful for debugging or simply monitoring client activity. Along with the STOMP frame itself the remote IP address of the client is logged as well as the internal connection ID so that frames from the same client can be correlated.

Sending

When a STOMP client sends a message (using a SEND frame), the protocol manager looks at the message to determine where to route it and potentially how to create the address and/or queue to which it is being sent. The protocol manager uses either of the following bits of information from the frame to determine the routing type:

  1. The value of the destination-type header. Valid values are ANYCAST and MULTICAST (case sensitive).

  2. The "prefix" on the destination header. See additional info on prefixes.

If no indication of routing type is supplied then the default defined in the corresponding default-address-routing-type & default-queue-routing-type address-settings will be used.

The destination header maps to an address of the same name. If the destination header used a prefix then the prefix is stripped.

Subscribing

When a STOMP client subscribes to a destination (using a SUBSCRIBE frame), the protocol manager looks at the frame to determine what subscription semantics to use and potentially how to create the address and/or queue for the subscription. The protocol manager uses either of the following bits of information from the frame to determine the routing type:

  1. The value of the subscription-type header. Valid values are ANYCAST and MULTICAST (case sensitive).

  2. The "prefix" on the destination header. See additional info on prefixes.

If no indication of routing type is supplied then the default defined in the corresponding default-address-routing-type & default-queue-routing-type address-settings will be used.

The destination header maps to an address of the same name if multicast is used or to a queue of the same name if anycast is used. If the destination header used a prefix then the prefix is stripped.

STOMP heart-beating and connection-ttl

Well behaved STOMP clients will always send a DISCONNECT frame before closing their connections. In this case the server will clear up any server side resources such as sessions and consumers synchronously. However if STOMP clients exit without sending a DISCONNECT frame or if they crash the server will have no way of knowing immediately whether the client is still alive or not. STOMP connections therefore default to a connection-ttl value of 1 minute (see chapter on connection-ttl for more information. This value can be overridden using the connection-ttl-override property or if you need a specific connectionTtl for your stomp connections without affecting the broker-wide connection-ttl-override setting, you can configure your stomp acceptor with the connectionTtl property, which is used to set the ttl for connections that are created from that acceptor. For example:

<acceptor name="stomp-acceptor">tcp://localhost:61613?protocols=STOMP;connectionTtl=20000</acceptor>

The above configuration will make sure that any STOMP connection that is created from that acceptor and does not include a heart-beat header or disables client-to-server heart-beats by specifying a 0 value will have its connection-ttl set to 20 seconds. The connectionTtl set on an acceptor will take precedence over connection-ttl-override. The default connectionTtl is 60,000 milliseconds.

Since STOMP 1.0 does not support heart-beating then all connections from STOMP 1.0 clients will have a connection TTL imposed upon them by the broker based on the aforementioned configuration options. Likewise, any STOMP 1.1 or 1.2 clients that don't specify a heart-beat header or disable client-to-server heart-beating (e.g. by sending 0,X in the heart-beat header) will have a connection TTL imposed upon them by the broker.

For STOMP 1.1 and 1.2 clients which send a non-zero client-to-server heart-beat header value then their connection TTL will be set accordingly. However, the broker will not strictly set the connection TTL to the same value as the specified in the heart-beat since even small network delays could then cause spurious disconnects. Instead, the client-to-server value in the heart-beat will be multiplied by the heartBeatConnectionTtlModifer specified on the acceptor. The heartBeatConnectionTtlModifer is a decimal value that defaults to 2.0 so for example, if a client sends a heart-beat header of 1000,0 the the connection TTL will be set to 2000 so that the data or ping frames sent every 1000 milliseconds will have a sufficient cushion so as not to be considered late and trigger a disconnect. This is also in accordance with the STOMP 1.1 and 1.2 specifications which both state, "because of timing inaccuracies, the receiver SHOULD be tolerant and take into account an error margin."

The minimum and maximum connection TTL allowed can also be specified on the acceptor via the connectionTtlMin and connectionTtlMax properties respectively. The default connectionTtlMin is 1000 and the default connectionTtlMax is Java's Long.MAX_VALUE meaning there essentially is no max connection TTL by default. Keep in mind that the heartBeatConnectionTtlModifer is relevant here. For example, if a client sends a heart-beat header of 20000,0 and the acceptor is using a connectionTtlMax of 30000 and a default heartBeatConnectionTtlModifer of 2.0 then the connection TTL would be 40000 (i.e. 20000 * 2.0) which would exceed the connectionTtlMax. In this case the server would respond to the client with a heart-beat header of 0,15000 (i.e. 30000 / 2.0). As described previously, this is to make sure there is a sufficient cushion for the client heart-beats in accordance with the STOMP 1.1 and 1.2 specifications. The same kind of calculation is done for connectionTtlMin.

The minimum server-to-client heart-beat value is 500ms.

Note:

Please note that the STOMP protocol version 1.0 does not contain any heart-beat frame. It is therefore the user's responsibility to make sure data is sent within connection-ttl or the server will assume the client is dead and clean up server side resources. With STOMP 1.1 users can use heart-beats to maintain the life cycle of stomp connections.

Selector/Filter expressions

STOMP subscribers can specify an expression used to select or filter what the subscriber receives using the selector header. The filter expression syntax follows the core filter syntax described in the Filter Expressions documentation.

STOMP and JMS interoperability

Sending and consuming STOMP message from JMS or Core API

STOMP is mainly a text-orientated protocol. To make it simpler to interoperate with JMS and Core API, our STOMP implementation checks for presence of the content-length header to decide how to map a STOMP 1.0 message to a JMS Message or a Core message.

If the STOMP 1.0 message does not have a content-length header, it will be mapped to a JMS TextMessage or a Core message with a single nullable SimpleString in the body buffer.

Alternatively, if the STOMP 1.0 message has a content-length header, it will be mapped to a JMS BytesMessage or a Core message with a byte[] in the body buffer.

The same logic applies when mapping a JMS message or a Core message to STOMP. A STOMP 1.0 client can check the presence of the content-length header to determine the type of the message body (String or bytes).

Message IDs for STOMP messages

When receiving STOMP messages via a JMS consumer or a QueueBrowser, the messages have no properties like JMSMessageID by default. However this may bring some inconvenience to clients who wants an ID for their purpose. The broker STOMP provides a parameter to enable message ID on each incoming STOMP message. If you want each STOMP message to have a unique ID, just set the stompEnableMessageId to true. For example:

<acceptor name="stomp-acceptor">tcp://localhost:61613?protocols=STOMP;stompEnableMessageId=true</acceptor>

When the server starts with the above setting, each stomp message sent through this acceptor will have an extra property added. The property key is amq-message-id and the value is a String representation of a long type internal message id prefixed with STOMP, like:

amq-message-id : STOMP12345

The default stomp-enable-message-id value is false.

Durable Subscriptions

The SUBSCRIBE and UNSUBSCRIBE frames can be augmented with special headers to create and destroy durable subscriptions respectively.

To create a durable subscription the client-id header must be set on the CONNECT frame and the durable-subscription-name must be set on the SUBSCRIBE frame. The combination of these two headers will form the identity of the durable subscription.

To delete a durable subscription the client-id header must be set on the CONNECT frame and the durable-subscription-name must be set on the UNSUBSCRIBE frame. The values for these headers should match what was set on the SUBSCRIBE frame to delete the corresponding durable subscription.

Aside from durable-subscription-name, the broker also supports durable-subscriber-name (a deprecated property used before durable-subscription-name) as well as activemq.subscriptionName from ActiveMQ 5.x. This is the order of precedence if the frame contains more than one of these:

  1. durable-subscriber-name
  2. durable-subscription-name
  3. activemq.subscriptionName

It is possible to pre-configure durable subscriptions since the STOMP implementation creates the queue used for the durable subscription in a deterministic way (i.e. using the format of client-id.subscription-name). For example, if you wanted to configure a durable subscription on the address myAddress with a client-id of myclientid and a subscription name of mysubscription then configure the durable subscription:

<addresses>
   <address name="myAddress">
      <multicast>
         <queue name="myclientid.mysubscription"/>
      </multicast>
   </address>
</addresses>

Handling of Large Messages with STOMP

STOMP clients may send very large frame bodies which can exceed the size of the broker's internal buffer, causing unexpected errors. To prevent this situation from happening, the broker provides a STOMP configuration attribute stompMinLargeMessageSize. This attribute can be configured inside a stomp acceptor, as a parameter. For example:

<acceptor name="stomp-acceptor">tcp://localhost:61613?protocols=STOMP;stompMinLargeMessageSize=10240</acceptor>

The type of this attribute is integer. When this attributed is configured, the broker will check the size of the body of each STOMP frame arrived from connections established with this acceptor. If the size of the body is equal or greater than the value of stompMinLargeMessageSize, the message will be persisted as a large message. When a large message is delievered to a STOMP consumer, the broker will automatically handle the conversion from a large message to a normal message, before sending it to the client.

If a large message is compressed, the server will uncompressed it before sending it to stomp clients. The default value of stompMinLargeMessageSize is the same as the default value of min-large-message-size.

Web Sockets

Apache ActiveMQ Artemis also support STOMP over Web Sockets. Modern web browsers which support Web Sockets can send and receive STOMP messages.

STOMP over Web Sockets is supported via the normal STOMP acceptor:

<acceptor name="stomp-ws-acceptor">tcp://localhost:61614?protocols=STOMP</acceptor>

With this configuration, Apache ActiveMQ Artemis will accept STOMP connections over Web Sockets on the port 61614. Web browsers can then connect to ws://<server>:61614 using a Web Socket to send and receive STOMP messages.

A companion JavaScript library to ease client-side development is available from GitHub (please see its documentation for a complete description).

The payload length of Web Socket frames can vary between client implementations. By default the broker will accept frames with a payload length of 65,536. If the client needs to send payloads longer than this in a single frame this length can be adjusted by using the stompMaxFramePayloadLength URL parameter on the acceptor.

The stomp-websockets example shows how to configure an Apache ActiveMQ Artemis broker to have web browsers and Java applications exchanges messages.