AsyncAPI: Asynchrone Kommunikation für IoT und Microservices meistern

Seite 2: Struktur von AsyncAPI-Dokumenten

Inhaltsverzeichnis

Ein AsyncAPI-Dokument beginnt mit allgemeinen Informationen über die spezifizierte Schnittstelle, etwa den Namen und der Version, sowie einem Beschreibungstext und gegebenenfalls Lizenzierungsinformationen. Darauf folgt eine Beschreibung der Server, über die die asynchrone Kommunikation mit der Schnittstelle laufen soll. Das kann zum Beispiel ein Message Broker sein oder auch ein Endpunkt, der WebSockets unterstützt. Für jeden Server lassen sich URL, Kommunikationsprotokoll und Protokollversion hinterlegen. Zudem ist es möglich, Server für unterschiedliche Stages zu definieren, etwa für Entwicklung, Test und Produktion. Auch Sicherheitsmechanismen und -anforderungen der einzelnen Server lassen sich im AsyncAPI-Dokument hinterlegen.

Im Zentrum der AsyncAPI-Beschreibung stehen die Channels, also die Kommunikationskanäle, die die Server für die Übertragung der Nachrichten bereitstellen. In einem typischen Message Broker sind es beispielsweise Topics oder Queues. Jeder Channel hat einen Namen sowie eine Publish- oder Subscribe-Operation mit zugehöriger Nachrichtenstruktur. Hierbei ist wichtig zu verstehen, dass die Operationen aus der Sicht des API-Clients beschrieben sind. Eine Publish-Operation beschreibt also Nachrichten, die vom API-Client zu senden sind, um mit der Schnittstelle zu kommunizieren. Die Anwendung, die die Schnittstelle bereitstellt, konsumiert dann die Nachrichten. Im Unterschied dazu beschreiben Subscribe-Operationen solche Nachrichten, die die API-Clients konsumieren, die aber vom API-Betreiber ausgehen.

Wie bereits erwähnt beherrscht AsyncAPI für das Beschreiben der Nachrichten- respektive Datenstrukturen unterschiedliche Schema-Sprachen – insbesondere JSON Schema. Stattdessen könnte aber beispielsweise auch ein Avro-Schema zum Einsatz kommen. Jede einzelne Nachricht kann Headers, einen Payload und darüber hinaus eine Message ID, eine Correlation ID oder einen Content-Type besitzen, um nur die wichtigsten Attribute zu nennen.

Eine praxisnahe Beispiel-Anwendung eines Flight-Monitor-Service ist im Blog der AsyncAPI-Webseite anschaulich beschrieben. Fluggäste, die automatisiert Benachrichtigungen erhalten möchten, falls sich Abflugzeit oder Flugsteig ändern, können sich dafür über ein Webformular registrieren (Subscriber). Die Subscriber-Anwendung sendet sodann eine asynchrone Nachricht über einen MQTT-Broker an eine Monitoring-Anwendung, um diese über die neue Registrierung und den betreffenden Flug zu informieren. Die Monitoring-Anwendung überwacht nun ihrerseits die aktuellen Flugpläne und nutzt hierzu die On Demand Flight Status API von Amadeus für den Zugriff auf Echtzeitdaten. Sobald sich die Daten eines Fluges ändern, für den Fluggäste Benachrichtigungen beauftragt haben, sendet die Monitoring-Anwendung eine asynchrone Nachricht an den Notifier.

Dessen Aufgabe ist es, alle registrierten Fluggäste per SMS über die Änderung der Flugdaten zu informieren. Sowohl die Monitoring-, als auch die Notifier-Anwendung stellen also eine asynchrone API bereit, die sich mittels AsyncAPI beschreiben lässt. Clients der Monitoring-Anwendung – in diesem Fall der Subscriber – müssen Nachrichten auf dem Channel flight/queue publizieren, um neue Registrierungen bekannt zu machen. Gleichzeitig können Clients der Monitoring-Anwendung deren Nachrichten auf dem Channel flight/update konsumieren. Eine beispielhafte AsyncAPI-Beschreibung ist in Listing 1 zu sehen. Die Schemata für die Strukturen der Nachrichten sind dabei in separate Dateien ausgelagert und auf GitHub einsehbar.

asyncapi: '2.0.0'
info:
  title: Flight Monitor Service
  version: '1.0.0'
  description: |
     provides real-time flight schedule data including up-to-date departure and arrival times, 
     terminal and gate information, flight duration and real-time delay status.
  license:
    name: Apache 2.0
    url: 'https://www.apache.org/licenses/LICENSE-2.0'
servers:
  development:
    url: mqtt://localhost:1883
    protocol: mqtt
channels:
  flight/update:
    description: |
      Provides updates from a subscribed flight
    subscribe:
      summary: Inform about the status of a subscribed flight
      message:
        $ref: '#/components/messages/flightStatus'
  flight/queue:
    description: |
      Queues a flight in order to retrieve status
    publish:
      summary: Subscribe about the status of a given flight
      message:
        $ref: '#/components/messages/flightQueue'
components:
  messages:
    flightStatus:
      $ref: '../common/messages/flight_status.yaml'
    flightQueue:
      $ref: '../common/messages/flight_queue.yaml'

Listing 1: AsyncAPI-Beschreibung des Flight Monitor Service

Beim Verwenden neuer Technologien stellt sich für Entwicklerinnen und Entwickler stets die Frage, wie es mit dem Tool-Support bestellt ist. AsyncAPI reicht in dieser Disziplin bei weitem (noch) nicht an OpenAPI heran. Die wichtigsten, von OpenAPI bekannten Werkzeuge sind jedoch bereits verfügbar. So existieren diverse AsyncAPI-Editoren für das manuelle Erstellen von AsyncAPI-Dokumenten, unter anderem als Erweiterungen für diverse IDEs, wie IntelliJ IDEA oder Visual Studio Code. Auch ein HTML-basierter Editor ist verfügbar, der sich wahlweise online öffnen oder alternativ lokal installieren lässt. Zum Generieren einer optisch ansprechenden HTML-Dokumentation der Schnittstelle aus einer gegebenen AsyncAPI-Spezifikation steht ein HTML-Renderer bereit. Beide sind online im AsyncAPI-Studio verfügbar – wie auch die hierbei verwendete React-Komponente.

Für das Generieren der HTML-Dokumentation ist der AsyncAPI-Generator verantwortlich. Er lässt sich mit beliebigen Templates bestücken, sodass neben der HTML-Dokumentation auch das Erstellen anderer Formate oder Code für unterschiedliche Programmiersprachen möglich ist, derzeit für Node.js, Java, Python, TypeScript, Go und C#. Code aus Schnittstellenspezifikationen zu erstellen, ist in vielen Entwicklungsteams seit Jahren gängige Praxis. Es setzt jedoch hinreichende Erfahrung mit solchen Technologien voraus und sollte nur mit Vorsicht zum Einsatz kommen. Denn erfahrungsgemäß führt derart generierter Code mittelfristig oftmals zu unschönen Nebenwirkungen.

Beispielsweise verhält er sich häufig nicht tolerant gegenüber unerwarteten, jedoch nicht benötigten Attributen in empfangenen Datenstrukturen (vgl. Tolerant Reader Pattern). Das hat zur Folge, dass eigentlich rückwärtskompatible Erweiterungen der Schnittstelle dennoch zu Laufzeitfehlern führen. Die Alternative zum Einsatz von Generatoren wäre das manuelle Implementieren des Codes, der die im AsyncAPI-Dokument spezifizierte Schnittstelle umsetzt. Das ist anfangs zwar aufwendiger, lohnt sich jedoch in aller Regel über den Einsatzzeitraum einer Schnittstelle. Unabhängig davon lässt sich der Generator mithilfe einer bereitgestellten GitHub Action auf einfache Weise in eigene Build-Pipelines integrieren.

AsyncAPI Editor und HTML-Renderer (Abb. 2).