Unpacking rumqttc: A Simple Guide to Its Internal Workings
rumqttc, part of the open-source rumqtt library collection developed in Rust, is a user-friendly, high-level MQTT client.
What to expect: In this post, we'll take a closer look at the inner workings of rumqttc, unraveling its core mechanics to understand how it efficiently manages MQTT communications.
Understanding Events in rumqttc
At the heart of rumqttc, every activity is signified by an Event
. These events are bifurcated into Event::Incoming
and Event::Outgoing
, indicating incoming and outgoing packets, respectively.
Is this the same Event
that is returned when we poll event loop?
Yes, When we poll the event loop, an Event
is returned. Let's see when an event is created and how Eventloop works.
When we delve into the event loop—the engine driving rumqttc—it juggles multiple tasks. It reads packets from the network, sends out periodic pings to keep the session active, and processes client-generated requests for sending packets. This multitasking powerhouse ensures that every action in rumqttc is responsive and efficient.
So is that why you must poll the event loop to make progress?
Correct. The event loop is the core of the client, like the heart of rumqttc.
First, we'll explore how incoming packets are processed. When the event loop receives packets from the network (broker), it initially manages them in line with MQTT standards, such as dispatching acknowledgments for published packets. Subsequently, an Event::Incoming(Packet) is added to the event loop's state for each packet.
Since packets are processed in groups, multiple packets lead to the creation of an individual event for each one. During the polling of the event loop, existing pending events in the state are prioritised and addressed immediately, before the loop attempts to retrieve new packets from the network.
So how do I send packets to the broker?
This is where Request
comes into the picture. Request
is basically a request to send a packet.
When there's a need to dispatch a packet to the network, it necessitates making a request to the event loop, which is encapsulated in a Request
. This Request
can embody any form of packet that a client might send to a broker in accordance with MQTT standards, such as publish, puback, subscribe, ping, disconnect, among others.
But how do I even create these requests?
To generate and dispatch requests to the event loop, the Client
is utilized in rumqttc. Constructing a client in rumqttc is done as follows:
let (client, eventloop) = AsyncClient::new(..);
Here, the new(..)
method yields a tuple comprising (AsyncClient, Eventloop)
. We've already covered Eventloop
, and AsyncClient
essentially serves as a conduit for directing Request
to the event loop. For instance, invoking client.
publish(..)
internally formulates a Request::Publish(..)
and forwards it to the event loop. This process is similar for other methods available on the client, such as .subscribe(..)
, .ack(..)
, .disconnect(..)
, and so on.
Unlike the AsyncClient
, Client::new(..)
returns Connection
instead of Eventloop
, is the synchronous client something different?
No, not really! Connection
simply encapsulates Eventloop
, allowing its use in your code without the need for asynchronous operations. Rather than using .poll()
, you can progress with connection.recv()
or connection.iter()
.
That's essentially what it boils down to!
So let’s summarize!
The Eventloop endeavors to read packets from the network, transmit pings, or handle requests for sending packets generated by the client. Throughout these processes, it employs Events to keep us informed about the specific activities taking place.
Join the rumqttc Community
rumqttc is not just a library; it's a community effort. Feel free to dive into the rumqttc docs, explore examples, and contribute to its development. Whether you're opening issues, submitting PRs, or simply starring the repository, your involvement helps build a fast, robust, and best-in-class MQTT client.
rumqttc's design cleverly abstracts the complexities of MQTT communication, offering a user-friendly interface that's both powerful and flexible. By understanding its core mechanics, you can leverage its full potential in your IoT projects. Happy coding!