MQTT Swiftlet
Features
The MQTT Swiftlet provides a full-featured MQTT 3.1/3.1.1 broker implementation. The following MQTT features are supported:
QoS 0 (at most once), QoS 1 (at least once), QoS 2 (exactly once).
Topic wildcard filters.
Retained messages.
Last Will and Testament.
Clean and persistent sessions.
Session takeover.
Packet replay on reconnect.
Keep Alive.
Additional features provided by the MQTT Swiftlet:
Full interoperability between MQTT, AMQP 1.0/0.9.1 and JMS clients.
Topic order guarantees for all QoS levels.
Persistent session timeout and automatic cleanup.
Administrative deletion of persistent sessions.
Plain and TLS connections.
Each feature is explained in more detail in the following chapters.
Quality of Service (QoS)
In MQTT the publisher of a message decides on the importance of a message by specifying the QoS in the publish package. The QoS levels are as follows:
QoS | Meaning | Description |
---|---|---|
0 | At Most Once | Messages can be lost. |
1 | At Least Once | Messages can't be lost but duplicates may occur. |
2 | Exactly Once | Messages are delivered once and only once. |
The interaction between the publisher and MQTT Swiftlet takes place according to the specified QoS level in the publish packet. A subscriber has its own QoS specified per subscription. This QoS level is the maximum level the subscriber supports. The QoS level for each message is compared to the QoS of the subscriber and maybe downgraded, if necessary. For example, if the publisher has specified QoS 2 and the subscriber only has QoS 1, the message is handled according to QoS 1. On the contrary, if the publisher specifies QoS 0 and the subscriber has QoS 2, QoS 0 is used.
Topic Wildcard Filters
In MQTT topics are structured as a hierarchy, for example, devices/device1/temp
. The forward slash /
is the delimiter. A subscriber can define a topic filter that is applied to the messages which are delivered to the subscription if the filter matches the topic under which the messages have been published.
MQTT defines 2 wildcard characters: +
addresses all messages of a hierarchy node and #
addresses all messages of a node and all sub-nodes, thus #
can only be specified as the last character of a filter.
MQTT topics are mapped to SwiftMQ topic predicates internally. SwiftMQ topics are a bit different as the topic delimiter is a .
such as devices.device1.temp
and the whole topic name can be a SQL LIKE predicate where the underscore _
matches any single character and the percent sign %
any string. SwiftMQ does not allow to specify a wildcard for the root node of a topic hierarchy.
In consequence, there are some limitations for MQTT topic filters:
MQTT Topic Filter | SwiftMQ Topic Predicate | Valid | Description |
---|---|---|---|
+ | - | No | Filter at the root node is not allowed. |
# | - | No | Filter at the root node is not allowed. |
+/+/temp | - | No | Filter at the root node is not allowed. |
/devices/+/temp | devices.%.temp | Yes | |
/devices/# | devices.% | Yes |
Retained Messages
A publisher can flag a message as "retain". The last retained message per topic is sent to new subscriptions of that topic as the very first message. This is a useful feature to e.g. store and communicate the last state sent by a device without the need that the device is currently connected.
The MQTT Swiftlet stores retained messages in memory. So after a reboot of a SwiftMQ Router or a failover of a SwiftMQ High Availability Router these retained messages are lost.
Last Will and Testament (LWT)
An MQTT client can specify an LWT message in the connect packet. This message will be sent to a topic when the client unexpectedly disconnects without sending a disconnect packet. For example, the network connection is lost or the client has disconnected administratively via SwiftMQ Explorer/CLI.
LWT messages can also be marked as "retain". See the previous chapter.
Sessions
In MQTT a session represents the state of a connection. It contains:
All topic subscriptions.
A replay log of unacknowledged packets.
An MQTT client specifies a "clean session" flag in the connect packet. If that is set, a previous persistent session including all active subscriptions is deleted and a new (clean) session is created. This clean session is temporary and has a lifetime of that connection. All subscriptions of a clean session are non-durable (backed by a temporary queue).
If the "clean session" flag is not set, the session is persistent. All subscriptions of a persistent session are durable (backed by a durable subscriber queue) and the messages are stored persistently. If a client disconnects, the session is stored persistently (survives a reboot and failover). If a client reconnects and there is a persistent session stored for this client id, the replay log is first sent to the client to finish unacknowledged packets (the client does the same) and the subscriptions are active without the need to subscribe. Message delivery starts immediately.
In case the persistent session is associated with a connection with the same client id (e.g. the previous TCP connection is still alive due to half-open sockets), this active connection is closed by the MQTT Swiftlet and the new connection is associated with the persistent session.
Keep-Alive
An MQTT client can send ping request packets. This will be answered by a ping response packet by the MQTT Swiftlet. Each MQTT connection has a keepalive timer. If a connection did not send a packet within 1.5 times of the value of the keepalive field in the connect packet, the connection will be closed by the MQTT Swiftlet.
Interoperability between MQTT, AMQP, JMS
SwiftMQ provides full interoperability between MQTT, AMQP and JMS clients.
The payload of an MQTT message is a stream of bytes. A message published to a topic is internally converted to a JMS BytesMessage. A subscription receives a JMS BytesMessage and converts it into an MQTT publish packet.
The resulting JMS BytesMessage from an MQTT publish contains the following message properties:
Name | Type | Value |
---|---|---|
| String | Username of the Publisher. |
| String | Client id of the Publisher. |
| Integer | QoS level of the Publisher. |
These properties can be used by JMS/AMQP clients in message selectors. The properties have an only informational purpose, except JMS_SWIFTMQ_MQTT_PUBQOS
which is used by MQTT subscribers to determine the resulting QoS for the subscription. If JMS_SWIFTMQ_MQTT_PUBQOS
is not set, the subscriber uses the QoS of the subscription.
Message Order Guarantees
The MQTT Swiftlet uses the pub/sub subsystem of the SwiftMQ Router. Subscriptions are backed by durable or non-durable queues which guarantee message order independent of the QoS level.
Persistent Session Timeout and Cleanup
Persistent sessions have a configurable timeout (default: 1 week). If they were not associated with a connection during this time, they are deleted including all subscriptions.
Plain and TLS Connections
Per default, the MQTT Swiftlet has 2 configured listeners. One for plain TCP connections and one for TLS connections. TLS connections are served by Java JSSE included in the Java distribution.
Management
Listener Configuration
An MQTT listener listens on a specific port and accepts MQTT connections. Per default the MQTT Swiftlet defines an MQTT listener on port 1883 for plain MQTT connections:
A listener is configured by a connection template which is referenced by its name. See next section.
A new listener on a different port can be created by selecting the Listeners
entity and clicking Create a new Entity
:
Connection Templates
Connection templates are pre-configured templates that are referenced from MQTT listeners. They are located under the Declarations
entity:
A connection template default
does not need to be created because it refers to a connection template with all default values. The above tls
connection template is a default template with a different socket factory (JSSESocketFactory for TLS).
A new connection template can be created by selecting the Connection Templates
entity and clicking Create a new Entity
:
Session Timeout
The session timeout is the time of inactivity of a persistent session where it is not associated with a connection. Sessions and their subscriptions are deleted when they reach the timeout. The timeout is in hours (default: 168 hours = 1 week) and can be configured here:
Monitoring
Monitoring MQTT Connections
MQTT connections are located under the Usage
section of the MQTT Swiftlet:
Start some MQTT clients and expand the Usage
section of the MQTT Swiftlet. The information shown is down to the subscriptions and message transfer per subscription can be observed.
Monitoring Queues
Another view can be opened on the Queue Manager Swiftlet's Usage
section. It shows the message throughput of the queues. Clean sessions use non-durable subscriber queues (tmp$...) while persistent sessions use durable subscriber queues (<clientid>$<number>
):
Monitoring Threadpools
The MQTT Swiftlet uses a single thread pool:
mqtt.connection
: Performs connection tasks and outbound writes.
The thread pool (and all others) can be observed in the Threadpool Swiftlet's Usage
section:
Configuration
The configuration of the MQTT Swiftlet is defined within the element
<swiftlet name="sys$mqtt" .../>
of the router's configuration file.
Attributes of Element "swiftlet"
Definition
Attribute | Type | Mandatory | Description |
---|---|---|---|
collect-interval | java.lang.Long | No | Collect Interval Messages/Sec |
session-timeout | java.lang.Long | No | Time in hours after which a unused Session is deleted |
Values
Attribute | Values |
---|---|
collect-interval | Default: 1000 |
session-timeout | Default: 168 |
Element "declarations", Parent Element: "swiftlet"
Declarations Section.
Element List "connection-templates", Parent Element: "declarations"
Templates for Connections. This element list contains zero or more "connection-template" elements with this template definition:
Definition
Attribute | Type | Mandatory | Description |
---|---|---|---|
name | java.lang.String | Yes | Name of this Connection Template |
socketfactory-class | java.lang.String | No | Socketfactory Class |
use-tcp-no-delay | java.lang.Boolean | No | Use Tcp No Delay |
idle-timeout | java.lang.Long | No | Inactivity timeout (ms) after which a Connection is disconnected |
max-message-size | java.lang.Integer | No | Maximum Message Size |
reject-disconnect-delay | java.lang.Long | No | Time (ms) after which a rejected Connection is closed |
router-input-buffer-size | java.lang.Integer | No | Router Network Input Buffer Size |
router-input-extend-size | java.lang.Integer | No | Router Network Input Extend Size |
router-output-buffer-size | java.lang.Integer | No | Router Network Output Buffer Size |
router-output-extend-size | java.lang.Integer | No | Router Network Output Extend Size |
Values
Attribute | Values |
---|---|
socketfactory-class | Default: com.swiftmq.net.PlainSocketFactory |
use-tcp-no-delay | Default: true |
idle-timeout | Default: 90000 |
max-message-size | Min: 0 |
reject-disconnect-delay | Min: 1000 |
router-input-buffer-size | Min: 65536 |
router-input-extend-size | Min: 65536 |
router-output-buffer-size | Min: 65536 |
router-output-extend-size | Min: 65536 |
Element List "listeners", Parent Element: "swiftlet"
Listener Definitions. This element list contains zero or more "listener" elements with this template definition:
Definition
Attribute | Type | Mandatory | Description |
---|---|---|---|
name | java.lang.String | Yes | Name of this Listener |
bindaddress | java.lang.String | No | Listener Bind IP Address |
port | java.lang.Integer | Yes | Listener Port |
max-connections | java.lang.Integer | Yes | Maximum Connections for Listener |
connection-template | java.lang.String | Yes | Connection Template to use |
Values
Attribute | Values |
---|---|
bindaddress | |
port | Default: 1883 |
max-connections | Default: -1 |
connection-template | Default: default |
Element List "host-access-list", Parent Element: "listener"
Host Access List. This element list contains zero or more "host-access-entry" elements with this template definition:
Definition
Attribute | Type | Mandatory | Description |
---|---|---|---|
name | java.lang.String | Yes | Name of this Host Access Entry |
Element "usage", Parent Element: "swiftlet"
Live Usage.
Element List "connections", Parent Element: "usage"
Active MQTT Connections. This element list contains zero or more "usage" elements with this template definition:
Definition
Attribute | Type | Mandatory | Description |
---|---|---|---|
name | java.lang.String | Yes | Name of this Active MQTT Connection |
client-id | java.lang.String | No | Client Id |
connect-time | java.lang.String | No | Connect Time |
msgs-received | java.lang.Integer | No | Messages/Sec received from Connection |
msgs-sent | java.lang.Integer | No | Messages/Sec sent to Connection |
total-received | java.lang.Integer | No | Total Number of Messages received from Connection |
total-sent | java.lang.Integer | No | Total Number of Messages sent to Connection |
username | java.lang.String | No | User Name |
mqtt-protlevel | java.lang.Integer | No | MQTT Protocol Level [4 = 3.1.1] |
Values
Attribute | Values |
---|---|
client-id | |
connect-time | |
msgs-received | Default: 0 |
msgs-sent | Default: 0 |
total-received | Default: 0 |
total-sent | Default: 0 |
username | |
mqtt-protlevel |
Element "MQTT Session", Parent Element: "usage"
MQTT Session.
Definition
Attribute | Type | Mandatory | Description |
---|---|---|---|
persistent | java.lang.Boolean | No | Persistent Session |
Values
Attribute | Values |
---|---|
persistent | Default: false |
Element List "subscriptions", Parent Element: "MQTT Session"
Topic Subscriptions. This element list contains zero or more "Topic Subscription" elements with this template definition:
Definition
Attribute | Type | Mandatory | Description |
---|---|---|---|
name | java.lang.String | Yes | Name of this Topic Subscription |
swiftmq-topic | java.lang.String | No | Translated SwiftMQ Topic Name |
qos | java.lang.Integer | No | Quality of Service |
msgs-received | java.lang.Integer | No | Messages/Sec received from Subscription |
total-received | java.lang.Integer | No | Total Number of Messages received from Subscription |
Values
Attribute | Values |
---|---|
swiftmq-topic | |
qos | Min: 0 |
msgs-received | Default: 0 |
total-received | Default: 0 |
Element List "sessions", Parent Element: "usage"
Persistent MQTT Sessions. This element list contains zero or more "MQTT Session" elements with this template definition:
Definition
Attribute | Type | Mandatory | Description |
---|---|---|---|
name | java.lang.String | Yes | Name of this MQTT Session |
associated | java.lang.Boolean | No | Associated with a Connection |
persistent | java.lang.Boolean | No | Persistent Session |
Values
Attribute | Values |
---|---|
associated | Default: false |
persistent | Default: false |
Element List "subscriptions", Parent Element: "MQTT Session"
Topic Subscriptions. This element list contains zero or more "Topic Subscription" elements with this template definition:
Definition
Attribute | Type | Mandatory | Description |
---|---|---|---|
name | java.lang.String | Yes | Name of this Topic Subscription |
swiftmq-topic | java.lang.String | No | Translated SwiftMQ Topic Name |
qos | java.lang.Integer | No | Quality of Service |
Values
Attribute | Values |
---|---|
swiftmq-topic | |
qos | Min: 0 |