Using the Qpid Proton C++ library to create a server for monitoring or telemetry data

Antenna logo Interfacing to sensors, or collecting low-level system health information, is a job that is often best done in C/C++. This kind of data collection is often platform-dependent, even when the data is exposed in a reasonably useable way (e.g., /dev/XXX devices on Linux).

However, we might want to make use of data like this using a different programming language, on a different host system from the one where it was collected. In a telemetry system, in fact, there could be hundreds of nodes collecting data, and a small number of places where the data is collected and aggregated.

A common way to handle requirements of this kind -- one that I have used myself many times -- is to run a message broker somewhere in the network. Data collection nodes would read data using whatever programming language is most suitable, and publish that data to the broker. Data analysis nodes would subscribe to the broker, and process the incoming data as they see fit. Modern message brokers can handle many different wire protocols, but they tend to be large, complex applications. Alternatively, Mosquitto is a good example of a lightweight MQTT message broker. It can even be run in an OpenShift cloud if needed. Mosquitto, however, supports only the MQTT protocol.

In this article I present an alternative method of data collection and distribution, that avoids the use of the message broker completely. In this approach, each telemetry node collects data, and makes it available using the AMQP messaging protocol. AMQP is widely-supported -- there are libraries for Java, C, C++, Python, Ruby, Go, and probably others. In effect, each telemetry node runs its own mini-broker, as part of the same application that collects the data. Clients can subscribe to "queues" on that broker, just as they would to a real broker.

The advantages of this approach are the elimination of a central broker, and wide language support. In addition, AMQP 1.0 is now an international standard, and is likely to remain stable and well-supported. Adding what amounts to a message broker to the data collection application creates surprisingly little overhead, when using the approach described here. Of course, you probably won't want to implement your own AMQP message broker from scratch -- what's needed is a software library that will allow us to do that.

In this article, I'm mostly concerned with Linux (perhaps embedded Linux). However, all the software used does run under Windows (but please don't ask me how).

About Qpid Proton

Apache Qpid Proton is a library in C and C++ for managing AMQP 1.0 messaging connections. It also has bindings for Ruby and Python, making it relatively easy to program AMQP applications in those languages. Using Proton does not require much knowledge of AMQP, although some is helpful.

Although Proton can be used to code clients of message brokers, it's equally applicable to creating servers. In fact, coding a client and coding a server are substantially similar. If you're used to programming in JMS, Proton might come as a shock, as it has very little conceptual overlap with the JMS system.

Getting Qpid Proton

Proton is available in the repositories of most popular Linux distributions. For Ubuntu-like systems:

$ sudo apt-get install libqpid-proton-cpp12-dev

On Fedora/RHEL, use:

$ sudo dnf install qpid-proton-cpp-devel

This is sufficient for C/C++ programming (although, of course, you'll need a suitable compiler). You might also install bindings for other programming languages, for example:

$ sudo dnf install python3-qpid-proton 

You can also build Proton from source, which isn't particularly difficult. If you do build from source, you'll get more control over which features are included, which might be important if your building for an embedded system.

The example

My example is a utility called amqp-monitor. All it does is broadcast an alert when the load average of the host system exceeds a certain threshold. Of course, this isn't a hugely useful application -- it's just an example of the kind of thing that can be done.

I'm not going to describe the source code of the example here. It is available from my GitHub repository, including a reasonably detailed description of how the program works, and how it uses Proton.

Running the data collection server

On the machine from which data is to be collected, run the server process like this:

$ amqp-monitor --cpu-load 20 --port 5672

The server will listen for incoming AMQP connections from data collection clients on (in this case) port 5672. Any number of clients can connect at the same time, and receive the same information. The server distributes its messages on a queue called "load", so this is what clients should connect to. It also distributes a timer tick on the queue called "tick", so that clients can test that they are connected properly.

Running the clients

Any client that can do AMQP can connect to the server on port 5672 (or whatever is selected), and subscribe to the queue called "load". Here's how to test this using my Java test client, amqutil:

$ amqutil subscribe --qpid --destination load --format text \
    --host [my server] --port 5672

For the record, the switch --qpid selects the use of the Qpid JMS library, for communicating using AMQP.

Alternatively, here is a code example in Python, which uses the Qpid Proton Python library for AMQP support:

 #!/usr/bin/env python3

    from proton.handlers import MessagingHandler
    from proton.reactor import Container

    address="localhost:5672/load"

    class Recv (MessagingHandler):
        def __init__ (self, url):
            super (Recv, self).__init__()
            self.url = url

        def on_start (self, event):
            event.container.create_receiver(self.url)

        def on_message (self, event):
                print (event.message.body)

    Container (Recv (address)).run()

Closing remarks

Running a message broker as a central place to collect and distribute telemetry or monitoring data is a widely-used practice, but it isn't the only way to handle such data. Embedding an AMQP server into the applications that collect data eliminates the need for a central broker, while still allowing the use of the widely-support AMQP messaging protocol.

Coding clients that collect the data is exactly the same, using this approach, as it would be with a central broker -- the clients just connect directly to the data collection nodes.