MQTT-Client in Java

MQTT-Client in Java

1. Überblick

In diesem Tutorial erfahren Sie, wie Sie MQTT-Nachrichten in einem Java-Projekt mithilfe der vonEclipse Paho project bereitgestellten Bibliotheken hinzufügen können.

2. MQTT-Grundierung

MQTT (MQ Telemetry Transport) is a messaging protocol, die erstellt wurden, um die Notwendigkeit einer einfachen und leichten Methode zum Übertragen von Daten zu / von Geräten mit geringem Stromverbrauch zu erfüllen, wie sie beispielsweise in industriellen Anwendungen verwendet werden.

Mit der zunehmenden Beliebtheit von IoT-Geräten (Internet of Things) hat die Verwendung von MQTT zugenommen und zu einer Standardisierung durch OASIS und ISO geführt.

Das Protokoll unterstützt ein einzelnes Nachrichtenmuster, nämlich das Publish-Subscribe-Muster: Jede von einem Client gesendete Nachricht enthält ein zugeordnetes „Thema“, das vom Broker zum Weiterleiten an abonnierte Clients verwendet wird. Themennamen können einfache Zeichenfolgen wie "oiltemp" oder eine pfadartige Zeichenfolge "motor/1/rpm" sein.

Um Nachrichten zu empfangen, abonniert ein Client ein oder mehrere Themen unter Verwendung seines genauen Namens oder einer Zeichenfolge, die eines der unterstützten Platzhalterzeichen enthält ("#" für Themen mit mehreren Ebenen und "+" für Themen mit einer Ebene).

3. Projektaufbau

Um die Paho-Bibliothek in ein Maven-Projekt aufzunehmen, müssen wir die folgende Abhängigkeit hinzufügen:


  org.eclipse.paho
  org.eclipse.paho.client.mqttv3
  1.2.0

Die neueste Version des Java-BibliotheksmodulsEclipse Pahokann von Maven Central heruntergeladen werden.

4. Client-Setup

Wenn Sie die Paho-Bibliothek verwenden, müssen wir zum Senden und / oder Empfangen von Nachrichten von einem MQTT-Broker zunächstobtain an implementation of the IMqttClient interface. Diese Schnittstelle enthält alle Methoden, die eine Anwendung zum Einrichten benötigt eine Verbindung zum Server herstellen, Nachrichten senden und empfangen.

Paho wird mit zwei Implementierungen dieser Schnittstelle ausgeliefert, einer asynchronen (MqttAsyncClient) und einer synchronen (MqttClient). In unserem Fall konzentrieren wir uns auf die synchrone Version, die einfachere Semantik hat.

Das Setup selbst ist ein zweistufiger Prozess: Wir erstellen zuerst eine Instanz der KlasseMqttClientund verbinden sie dann mit unserem Server. Der folgende Unterabschnitt beschreibt diese Schritte.

4.1. Erstellen einer neuenIMqttClient-Instanz

Das folgende Codefragment zeigt, wie eine neue synchrone Instanz vonIMqttClienterstellt wird:

String publisherId = UUID.randomUUID().toString();
IMqttClient publisher = new MqttClient("tcp://iot.eclipse.org:1883",publisherId);

In diesem Fallwe’re using the simplest constructor available, which takes the endpoint address of our MQTT broker and a client identifier, das unseren Kunden eindeutig identifiziert.

In unserem Fall haben wir eine zufällige UUID verwendet, sodass bei jedem Lauf eine neue Client-ID generiert wird.

Paho bietet auch zusätzliche Konstruktoren, mit denen wir den Persistenzmechanismus zum Speichern nicht bestätigter Nachrichten und / oder dieScheduledExecutorServiceanpassen können, die zum Ausführen von Hintergrundaufgaben verwendet werden, die für die Implementierung der Protokoll-Engine erforderlich sind.

The server endpoint we’re using is a public MQTT broker hosted by the Paho project, mit dem jeder mit einer Internetverbindung Clients testen kann, ohne dass eine Authentifizierung erforderlich ist.

4.2. Verbinde mit dem Server

Unsere neu erstellteMqttClient-Instanz ist nicht mit dem Server verbunden. We do so by calling its connect() method, wobei optional eineMqttConnectOptions -Sinstance übergeben wird, mit der wir einige Aspekte des Protokolls anpassen können.

Insbesondere können wir diese Optionen verwenden, um zusätzliche Informationen wie Sicherheitsanmeldeinformationen, Sitzungswiederherstellungsmodus, Wiederverbindungsmodus usw. weiterzugeben.

Die KlasseMqttConnectionOptionsmacht diese Optionen als einfache Eigenschaften verfügbar, die wir mit normalen Setter-Methoden festlegen können. Wir müssen nur die Eigenschaften festlegen, die für unser Szenario erforderlich sind - die übrigen nehmen Standardwerte an.

Der Code zum Herstellen einer Verbindung zum Server sieht normalerweise folgendermaßen aus:

MqttConnectOptions options = new MqttConnectOptions();
options.setAutomaticReconnect(true);
options.setCleanSession(true);
options.setConnectionTimeout(10);
publisher.connect(options);

Hier definieren wir unsere Verbindungsoptionen so, dass:

  • Bei einem Netzwerkausfall versucht die Bibliothek automatisch, die Verbindung zum Server wiederherzustellen

  • Es werden nicht gesendete Nachrichten aus einem vorherigen Lauf verworfen

  • Das Zeitlimit für die Verbindung beträgt 10 Sekunden

5. Nachrichten senden

Das Senden von Nachrichten mit einem bereits verbundenenMqttClient ist sehr einfach. We use one of the publish() method variants to send the payload, which is always a byte array, to a given topic unter Verwendung einer der folgenden Optionen für die Dienstqualität:

  • 0 - Semantik „höchstens einmal“, auch als „Feuer und Vergessen“ bekannt. Verwenden Sie diese Option, wenn der Nachrichtenverlust akzeptabel ist, da keine Bestätigung oder Persistenz erforderlich ist

  • 1 - "mindestens einmal" Semantik. Verwenden Sie diese Option, wenn der Nachrichtenverlust nicht akzeptabel ist.and Ihre Abonnenten können Duplikate verarbeiten

  • 2 - Semantik „genau einmal“. Verwenden Sie diese Option, wenn der Nachrichtenverlust nicht akzeptabel ist.and Ihre Abonnenten können keine Duplikate verarbeiten

In unserem Beispielprojekt spielt dieEngineTemperatureSensor -Skala die Rolle eines Scheinsensors, der jedes Mal, wenn wir diecall()-Methode aufrufen, einen neuen Temperaturmesswert erzeugt.

Diese Klasse implementiert dieCallable-Schnittstelle, sodass wir sie problemlos mit einer derExecutorService-Implementierungen verwenden können, die imjava.util.concurrent-Paket verfügbar sind:

public class EngineTemperatureSensor implements Callable {

    // ... private members omitted

    public EngineTemperatureSensor(IMqttClient client) {
        this.client = client;
    }

    @Override
    public Void call() throws Exception {
        if ( !client.isConnected()) {
            return null;
        }
        MqttMessage msg = readEngineTemp();
        msg.setQos(0);
        msg.setRetained(true);
        client.publish(TOPIC,msg);
        return null;
    }

    private MqttMessage readEngineTemp() {
        double temp =  80 + rnd.nextDouble() * 20.0;
        byte[] payload = String.format("T:%04.2f",temp)
          .getBytes();
        return new MqttMessage(payload);
    }
}

The MqttMessage encapsulates the payload itself, the requested Quality-of-Service and also the retained flag for the message. Dieses Flag zeigt dem Broker an, dass er diese Nachricht behalten soll, bis sie von einem Abonnenten verbraucht wird.

Wir können diese Funktion verwenden, um ein Verhalten zu implementieren, bei dem ein neuer Teilnehmer eine Verbindung zum Server herstellt und die gespeicherte Nachricht sofort erhält.

6. Nachrichten empfangen

Um Nachrichten vom MQTT-Broker zu empfangen,we need to use one of the subscribe() method variants, mit denen wir Folgendes angeben können:

  • Ein oder mehrere Themenfilter für Nachrichten, die wir empfangen möchten

  • Die zugehörige QoS

  • Der Callback-Handler zum Verarbeiten empfangener Nachrichten

Im folgenden Beispiel zeigen wir, wie Sie einer vorhandenenIMqttClient-Instanz einen Nachrichtenlistener hinzufügen, um Nachrichten von einem bestimmten Thema zu empfangen. Wir verwenden einCountDownLatch als Synchronisationsmechanismus zwischen unserem Rückruf und dem Hauptausführungsthread und dekrementieren es jedes Mal, wenn eine neue Nachricht eintrifft.

Im Beispielcode haben wir eine andereIMqttClient-Instanz verwendet, um Nachrichten zu empfangen. Wir haben es nur getan, um klarer zu machen, welcher Client was tut. Dies ist jedoch keine Paho-Einschränkung. Wenn Sie möchten, können Sie denselben Client zum Veröffentlichen und Empfangen von Nachrichten verwenden:

CountDownLatch receivedSignal = new CountDownLatch(10);
subscriber.subscribe(EngineTemperatureSensor.TOPIC, (topic, msg) -> {
    byte[] payload = msg.getPayload();
    // ... payload handling omitted
    receivedSignal.countDown();
});
receivedSignal.await(1, TimeUnit.MINUTES);

Die oben verwendete Variante vonsubscribe()verwendet eine Instanz vonIMqttMessageListenerals zweites Argument.

In unserem Fall verwenden wir eine einfache Lambda-Funktion, die die Nutzlast verarbeitet und einen Zähler dekrementiert. Wenn im angegebenen Zeitfenster (1 Minute) nicht genügend Nachrichten eintreffen, löst die Methodeawait()eine Ausnahme aus.

Bei Verwendung von Paho müssen wir den Empfang von Nachrichten nicht explizit bestätigen. Wenn der Rückruf normal zurückkehrt, geht Paho von einem erfolgreichen Verbrauch aus und sendet eine Bestätigung an den Server.

Wenn der Rückruf einException auslöst, wird der Client heruntergefahren. Please note that this will result in loss of any messages sent with QoS level of 0.

Mit QoS-Stufe 1 oder 2 gesendete Nachrichten werden vom Server erneut gesendet, sobald der Client erneut verbunden ist und das Thema erneut abonniert.

7. Fazit

In diesem Artikel haben wir gezeigt, wie wir mithilfe der vom Eclipse Paho-Projekt bereitgestellten Bibliothek Unterstützung für das MQTT-Protokoll in unseren Java-Anwendungen hinzufügen können.

In dieser Bibliothek werden alle Protokolldetails auf niedriger Ebene verarbeitet, sodass wir uns auf andere Aspekte unserer Lösung konzentrieren können. Gleichzeitig bleibt ausreichend Platz, um wichtige Aspekte der internen Funktionen, wie z. B. die Persistenz von Nachrichten, anzupassen.

Der in diesem Artikel gezeigte Code istover on GitHub verfügbar.