MQTT: An Implementer's Perspective
vasters.comI've been working on a project to do building monitoring. I'm building a library in C that talks to an Akka/Scala backend.
I initially chose MQTT to connect from the C library (that runs on the device) to the Akka system, via RabbitMQ's support for MQTT.
As I get deeper into the implementation, I'm thinking about ripping out MQTT. I just don't know what purpose it has. I think a better pattern is to have some kind of TCP terminator that the client/device connects to, which then fans out into an AMQP-like messaging system. Pushing messaging away from the core, far towards the edge doesn't seem like a good idea in environments where you really care about whether something is up/connected, and also, the wire protocol you use from the cloud to your device can be a lot different from that which you'd use in a core/cloud/racked messaging system.
I also don't care for MQTT's security model.
All in all, I'm pretty disappointed with it as a protocol. I'd take something written in protobuf and well-documented any day over mqtt.
My project, in case anyone's interested: https://github.com/prefiat/iotelemetri-feederd
This write up is right on the mark. There are quite a few things about MQTT that seem to have the goal of supporting legacy expediency. At the same time, these dubious design elements undermine the nominal goals of the standard and in some cases flat out contradict those goals.
We know how to design modern, efficient, flexible wire protocols that can be driven at the rates required by M2M communication patterns. MQTT does not fit this description nor even makes an attempt to reflect best practices within its current scope.
For the life of me I can't find anywhere that says "extensibility" is a goal of MQTT. The main goals are clearly stated to be "simplicity" and "lightweight". Extensibility is the enemy of both. A lot of Clemens' points are based on the concept that MQTT is supposed to be extensible. When you realize that's not even a goal, most of his concerns become moot. He says "I'm not trying to create another AMQP" but if you think about what he's actually saying, it's quite the opposite.
There are not many modern app-level protocols and much fewer messaging protocols that don't allow for any end-to-end message metadata; at a minimum declaring what the payload content of a message is, when the message was generated, and an end-to-end ID. Content-type could be a mere byte the client sends to the app and where 0x01 spells "JSON". CONNECT shows how you can have a bitfield that can control the presence of headers. You can control the presence of 4 subsequent optional headers with 4 shape options in one byte. That's as simple as what happens in CONNECT and just as compact.
There are also not many modern app-level protocols that constrain their opcode-set to 4 bits quasi forever, so extensibility also means to have runway for future extension.
> There are not many modern app-level protocols and much fewer messaging protocols that don't allow for any end-to-end message metadata; at a minimum declaring what the payload content of a message is, when the message was generated, and an end-to-end ID. Content-type could be a mere byte the client sends to the app and where 0x01 spells "JSON".
Sure, and suddenly you're limited to 256 content types, or, more likely, burning an extra byte and still frequently needing to communicate the actual substantive content-type in the payload. If you are going to bother having content-type be part of the messaging protocol, do it right rather than by half-measures.
As I see it, there's a continuum of messaging protocols from barebones (requiring smarter components) to full featured (that require the components to do less because the protocol and broker do more). ØMQ sits close to the first extreme -- call it "the left" -- and AMQP pretty close to what would then be "the right" (at least, the closest of any current open protocol that I am aware of); MQTT seems to be just to the right of ØMQ (I mean, it has a broker).
> A lot of Clemens' points are based on the concept that MQTT is supposed to be extensible.
No, I think they are based on the combination of factors:
(1) A lot of MQTT choices violate the stated design goals in a way which makes sense only to support extensibility,
(2) However, the specific way that MQTT specifies these things also makes them useless for extensibility.
(Plus, an extra serving of speculation about IBM's motivations heaped on top.)
Do you have some ideas for an alternative? Is it plausible, for example, to extend CoAP to cover MQTT use cases?
He says:
"Unfortunately, MQTT defines this assurance model only for the publish gesture and not for its own, inherent operations.."
OK, but then says:
"That means the client is forced to retry under any such circumstances. The specification already anticipates this with making subscriptions effectively idempotent and requiring that a subscribe command matching an existing subscription must be replaced by a new subscription and that the message flow must not be interrupted. "
So what exactly is the problem? If the client doesn't get the SUBACK it retries. The server has to deal with it. If the server gets a SUBSCRIBE, it has to be durable because the client might have received the previous SUBACK.
Why add one more turn around by acking the ack? This seems like extra complexity for no reason.
Also he talks about lack of flow control:
"Not having any flow control can get fairly dicey when there is significant work to do for processing a message and there might be several different subsystems on the receiving end of the MQTT connection where the work differs."
But there is flow control: it's TCP. Do not read from the TCP stack unless you can handle the message, then the flow control will work. On the other hand there is no concept of trying to multiplex multiple streams over one connection (where then you start to worry about head of line blocking). If you need this, open a different TCP connection for each stream. This is not an unreasonable design choice to make...
How do you know if you can handle the message, until you read the message? Or is MQTT's flow control inherently insufficient for cases like that?
Basic example, say for a chat client: to ensure delivery, you probably want to read the message, save it to a database, then confirm receipt. Is that possible, or would that delayed-ack have to be done via some other means?
That's the normal use case for QoS 1 and 2 messages: the client must acknowledge each received message otherwise the server will try to resend it. From the MQTT V3.1 Protocol Spec: http://public.dhe.ibm.com/software/dw/webservices/ws-mqtt/mq...
What you don't want to do is read everything offered into memory faster than you can write it to disk / database. You could potentially fill the memory until you run out and crash. Instead control the flow by not reading until you are finished with the previous message (or better, allow N unacknowledged messages to be read, where N is just enough so that the application is as fast as it can be).
Great write up! We found many of the same issues as you did when we tried connecting devices, so we created dweet.io.
We are not a substitute for MQTT, but we made dweet to help you get your things online quickly and easily so you can concentrate on making apps. Standards take forever to hash out, so we made something that can get you going really quickly.
Have you had a chance to look into dweet? https://dweet.io is the homepage, so let me know if you have any questions.
Thank you again
This comment's sales pitch tone really comes to a crescendo around "Have you had a chance to look into dweet?", just FYI you may want to avoid this type of language in the future.
So what's wrong with that? He built a service and he's suggesting that someone who might have an interest take a look at it.
"Build stuff, tell people about it."
We selected MQTT because our mobile apps are sending frequent data packets over cell-phone data tariffs. Our thinking was that a light messaging protocol such as MQTT could mean the difference between an £100,000+ monthly data bill, or £10,000.
I'm no expert, but I'm finding MQTT pretty bare bones, I can see the appeal of STOMP or messaging protocols that have more support for patterns such as RPC.
I'm so glad this was published because I had essentially come to the same result and thought I was missing something given all the hype around MQTT.
Btw there is a RabbitMQ plugin that does MQTT
What an extremely well thought-out piece. There is a certain bias towards MQTT’s suitability for building Azure-scale services (the article says as much), but I just wanted comment on a few general points.
> you cannot return connack (which confirms establishing or recovering a session) until all server nodes have access to a replica of this fact. The spec doesn't say that.
The spec should not have to say that. If you're building high-availability, distributed brokers (which MQTT makes no claims about), the semantic implications of each message should certainly be considered carefully, but a protocol specification is not a "how-to" guide enumerating every use-case. A brief note calling into consideration special cases may be welcome, but should not be required.
> With a "pull" based model that separates establishing subscriptions and message solicitation, you can leave delivery resumption control to the client, with MQTT those two aspects are coupled.
This behavior is a non-issue if the clear session flag is set. If it's not being set, the assumption is that subscription state is actively managed by the server. Leaving more work to the client is half-hearted and would complicate the protocol into something like AMQP’s. (I have thoughts about whether pull models are best suited for stateless operation, but that’s neither here nor there.)
Client IDs are indeed mis-named. To be fair, the 3.1.1 spec allows for server-assigned client ids. That doesn't completely mitigate the security risk, as user credentials are allowed to be shared among endpoints. This can be “fixed” at the broker level by requiring unique credentials, depending on the use case — it likely wouldn’t work for Azure.
The objection to the (lack of a) payload encoding is unclear. Being payload-agnostic actually a nice feature, especially on the broker side. Crucially — and please correct me if I'm wrong — MQTT is not designed as a generic mechanism to communicate with unknown clients, vis-a-vis HTTP. Clients are assumed to have awareness of the payload structure. Metadata can be further signified via a structured topic (again, clients know the format of the topic).
The article calls the structured-topic approach a hack, but it is a easy, readable method to impose simple structure or namespacing, and allows for things like metadata-based routing without any special effort from the broker. Hence, the need for explicit metadata (content-type, etc) seems unnecessary.
Regarding flow control — unless the spec declares otherwise, the broker is free to implement specific policies regarding the number of in-flight messages. For example, Mosquitto handles ACLs out-of-band. Otherwise, you can rely on TCP buffers to limit in-flight messages, or delay a QoS response until the next message can be handled. This also applies to data management policies, e.g. state or message retention, QoS-2 allowances, and so forth. Spec ambiguity is not necessarily a bad thing here. Over-specifying could constrain other use-cases; the insistence on “watertight assurances” doesn’t always hold water.
Multiplexing is not really an issue, unless you are sending large payloads — although if you are, I would venture to say you’re not using the right protocol anyway. I will place fault with the protocol for allowing 256MB payloads — this really just goes to show the incongruity between MQTT’s frugality with headers and wastefulness everywhere else.
All this assumes the user is using MQTT as it was designed: for simple pub-sub. What it allows out-of-the-box may not be flexible enough for many use cases. These problems can be mostly fixed with small changes to the protocol, or fixating on certain assumptions. This usually necessitates writing/modifying your own broker, but fortunately MQTT is simple enough that it isn’t a heroic deed. Of course, this doesn’t apply if you’re building a SaaS broker like Azure — but for any other purpose, MQTT can be viewed as a springboard.
Granted, all this sounds like it requires thinking too much about details that should otherwise be solved by things like AMQP. Maybe so. However, more sophisticated systems have many more knobs to tune, and the complexity rises accordingly. MQTT doesn’t have that problem out-of-the-box.