After various encounters and two projects which employed MQTT, I thought it was a good idea to write a summary about my experiences and my point of view. As my new job is very close to the IoT and Industry 4.0 sector, it did not take much time to encounter the hype. Once upon a time
MQTT is, like AMQP, a messaging protocol for distributed systems, meaning that a node can pass a message to one or multiple other nodes. Having used RabbitMQ and AMQP for years, this principle is nothing seriously new to me, and especially as a RMQ user, a core point of MQTT does not sound very exciting at the first glance - its simplicity. In AMQP, I have a whole bunch of functionality to get messages from A to B, including advanced routing and the, pardon me, awesome exchange functionality. MQTT lacks all of them - in exchange for a very little basic footprint, which makes it very tempting for embedded systems and little devices (such as sensors…) which do not possess much memory.
In the basic version of MQTT, developed in the late 1990s, the functionality was just publishing a Message M from a client C to a Broker B, which would dispatch it to all Clients S0..Sn which expressed interest in the type of message. A message M was defined as {Topic T, Payload P, QOS Q}, both parts of the tuple were represented as String. A broker B would wait for a set of Subscriptions S0..Sn, do just do a simple strcmp on any new message and dispatch the message to all clients which subscribed to the topic of a new message in the first place. Using a simple QOS mechanism, the publisher of a message was able to specify the way the Broker should deal with messages on a per-message basis: fire-and-forget (and do not care), deliver to at least one client, deliver to >1 clients.
On the transport side, MQTT could be sent through plain wire (such as a RS-232 / RS-485 line or even teletype), or via TCP/IP, of course unencryted and unauthorized.
So, the protocol was plain simple and a real minimalistic approach to setting up a message exchange, by no ways applicable to our needs in the year 2016 and should be forgotten, right?
Wrong.
My first applications with MQTT
My first experience with MQTT was in 2010, when I used it as a transport protocol for sensor data over a RS-485 line.
The project was a greenfield project, but the controller in the sensor was very limited, and my Atmel AtTiny had much more to care about than parsing, verifying and deserializing fancy XML data even for the transport layer. Using full-blown frameworks such as Apache Thrift was not an option aswell, but MQTT was deployed very fast. Using topic names “config.sensitivity”, “config.sensorType”, “sensor[0].value” and so on made the evaluation of data very easy, not to say trivial. On a 2-pin cable inside a machine cabinet, I also did not care very much about security, so it just made my live easier and saved me some hours of work to implement my own protocol. Cool, job done.
Then came the second, which is mainly used for telemetry and event exchange with other devices, using more advanced features of todays MQTT implementations:
- TLS/SSL
- Server and client certificates, automatically rolled out during device production, respectively orchestration of the broker server - * which means: Authentication by certificates. Of course MQTT also supports plain username/passwords.
- Hierarchical topics. Topics are not only strings anymore - following the convention of naming them in url-pattern style, such as devices/* /events/updates/caseTemperature, it is possible to perform registrations on a hierarchical basis. For example, a subscriber * registered for devices//* would also receive messages published to sub-topics such as the above.
- Which brings us to Access control lists, supported by a subset of the mainstream brokers. For example, why should a sensor be able to * subscribe to its own readings? Or, more relevant, why shouldn’t one share a MQTT broker with other clients, as long as they are not * likely to interfere?
- Distributed, scalable brokers.
Which means, one could even use MQTT on the internet nowadays without being fired.
Implementations
Client
On the client side, I mainly used Eclipse Paho. After writing a Autocloseable Fluent-API facade for it, it was quite pleasant to work with.
Examples (Java)
Connect to a Broker
1try
2(MQTTClientDecorator client = new MQTTClientDecorator()
3 .withBrokerUri("tcp://broker")
4 .withClientname("sensor:A0B0C0:A0")
5 .withCredentials("testuser", "passwd")
6 .withInitialConnection()
7 .create())
8{
9 LOG.log(Level.INFO, "test=tryBuildAndConnect_succeed connected={0} server={1}",
10 new Object[]
11 {
12 client.get().isConnected(), client.get().getCurrentServerURI()
13 });
14}
15catch (MqttException ex)
16{
17 LOG.log(Level.SEVERE, null, ex);
18 Assert.fail(ex.getLocalizedMessage());
19}
Or with SSL:
1try
2(MQTTClientDecorator client = new MQTTClientDecorator()
3 .withBrokerUri("ssl://broker:8883")
4 .withClientname("client")
5 .withCredentials("testuser", "passwd")
6 .withInitialConnection()
7 .withSsl("serverkeystore.jks", "passwd")
8 .create()) {
9
10 LOG.log(Level.INFO, "test=tryBuildAndConnect_succeed connected={0} server={1}",
11 new Object[]
12 {
13 client.get().isConnected(), client.get().getCurrentServerURI()
14 });
15}
16catch (MqttException ex)
17{
18 LOG.log(Level.SEVERE, null, ex);
19 Assert.fail(ex.getLocalizedMessage());
20}
Publishing a message:
1MqttMessage message = new MqttMessage("fired and forgotten".getBytes());
2message.setQos(0);
3// Client implements Optional<>
4client.get().publish("demo/test/fireAndForget", message);
Simple publish-receive test:
1@Test
2public void publishMessage_receiveIt() throws MqttException, InterruptedException
3{
4 CountDownLatch messageReceivedSignal = new CountDownLatch(1);
5
6 try (MQTTClientDecorator consumer = new MQTTClientDecorator()
7 .withBrokerUri("tcp://broker")
8 .withClientname("consumer")
9 .withInitialConnection()
10 .create();)
11 {
12 consumer.get().subscribe("demo/test/pubsub", (String topic, MqttMessage message) ->
13 {
14 LOG.log(Level.INFO, "Received message topic={0} message={1}",
15 new Object[]{topic, message.toString()});
16 messageReceivedSignal.countDown();
17 });
18
19 try (MQTTClientDecorator client = new MQTTClientDecorator()
20 .withBrokerUri("tcp://broker")
21 .withClientname("client")
22 .withInitialConnection()
23 .create())
24 {
25 MqttMessage message = new MqttMessage("Hej!".getBytes());
26 message.setQos(0);
27 client.get().publish("demo/test/pubsub", message);
28
29 LOG.info("Waiting for consumer to confirm message arrival..");
30 messageReceivedSignal.await(3, TimeUnit.SECONDS);
31 Assert.assertEquals(0, messageReceivedSignal.getCount());
32 LOG.info("Message arrival confirmed!");
33 }
34 }
35}
Broker
On the broker side, I used Moquette (easily embeddable into own Java applications) and Mosquitto. As there are excellent documentations on both, I will not go into details here. Conclusion
We have been through many approaches to make computers talk to each other. We have been through the SOAP-Ceremony, the CORBA-Hell, and now we even manage to write beautiful RESTful webservices. We also laughed about Windows NT messaging. Personally, I like to model communication architectures using RabbitMQ, but I also have to confess that often a simple messaging protocol without any advanced features would have done the job.
On the other hand, I do not think much of hypes and consultants who try to make a fortune on selling the next best thing. So, I will just add MQTT to my toolbox, be happy with the current exciting project which employs MQTT for M2M and telemetry, and, as usual, choose the right tool for the job, with or without a fancy title, next time again. As MQTT will gain more use-cases just because of simplicity, I do not think this will be our last encounter.