Definition

Reaktive Programmierung

Reaktive Programmierung ist ein Designparadigma, das sich auf asynchrone Programmierlogik stützt, um Echtzeit-Updates von ansonsten statischen Inhalten zu verarbeiten. Es bietet ein effizientes Mittel – die Verwendung von automatisierten Datenströmen – um Datenaktualisierungen von Inhalten zu verarbeiten, wenn ein Benutzer eine Anfrage stellt.

Datenströme, die in der reaktiven Programmierung verwendet werden, sind kohärente, zusammenhängende Sammlungen von digitalen Signalen, die kontinuierlich oder nahezu kontinuierlich erzeugt werden. Diese Datenströme werden von einer Quelle – zum Beispiel einem Bewegungssensor, einem Temperaturmesser oder einer Produktbestandsdatenbank – als Reaktion auf einen Auslöser gesendet. Dieser Auslöser kann einer der folgenden sein:

  • Ein Event wie softwaregenerierte Alarme, Tastenanschläge oder Signale von einem IoT-System (Internet of Things).
  • Ein Call, der eine Funktion ist, die eine Routine als Teil eines Workflows aufruft.
  • Eine Nachricht, das heißt eine Informationseinheit, die das System mit Informationen über den Status eines Vorgangs, eines Fehlers, einer Störung oder eines anderen Zustands an den Benutzer oder Systembetreiber zurücksendet.

Bei der reaktiven Programmierung wird Software erstellt, die auf Events reagiert, anstatt Eingaben vom Benutzer zu verlangen. Ein Event ist ein Signal, dass etwas passiert ist. Es ist allgemein anerkannt, dass Events Echtzeit-Signale sind, das heißt sie werden zeitgleich mit dem Zustand, den sie signalisieren, erzeugt und müssen auch in Echtzeit verarbeitet werden. Diese Events lassen sich am besten als Streams (Datenströme) visualisieren, die durch mehrere Verarbeitungselemente fließen, unterwegs angehalten und verarbeitet werden oder sich gabeln und parallele Verarbeitungsaktivitäten erzeugen. In den meisten Fällen ist diese Verarbeitung zeitkritisch, was bedeutet, dass die Anwendungen einen anderen Programmierstil erfordern, wodurch reaktive Programmierung entstanden ist.

Die reaktive Programmierung und die damit verbundenen reaktiven Systeme bestehen aus einer Kombination von Observer- und Handler-Funktionen. Ersterer erkennt wichtige Zustände oder Änderungen und generiert Nachrichten, um zu signalisieren, dass sie eingetreten sind, und letzterer geht mit diesen Nachrichten entsprechend um. Die Annahme bei der reaktiven Programmierung ist, dass es keine Kontrolle über die Anzahl oder den Zeitpunkt der Events gibt, daher muss die Software belastbar und hoch skalierbar sein, um variable Lasten zu bewältigen.

Frühe Anwendungen der reaktiven Programmierung für Geschäftsanwendungen beschränkten sich weitgehend auf Dinge wie die Überwachung des Zustands von Netzwerken, Servern oder Software und die Signalisierung von Datenbankzuständen wie zum Beispiel Lagerbeständen. Dieser Schwerpunkt ändert sich mit dem Aufkommen des Internets der Dinge, Smart Buildings und Smart Cities sowie Public Cloud Computing. Das Internet der Dinge hat das reaktive Modell in der Gebäudeverwaltung, der industriellen Prozesssteuerung und sogar der Heimautomatisierung wichtig gemacht. Die Cloud hat sowohl einen Stil der Komponentisierung von Software eingeführt – funktionales Computing und Microservices – als auch eine Bewegung, viele reaktive Anwendungen aufgrund ihrer Skalierbarkeit und Zuverlässigkeit in die Cloud zu verlagern.

Wie funktioniert reaktive Programmierung?

Bei der reaktiven Programmierung dreht sich alles um Streams, das heißt zeitlich geordnete Sequenzen von zusammenhängenden Event-Meldungen. Ein gegebener Stream beginnt in der Regel mit einem Observer (Beobachter), der entweder ein Codesegment innerhalb einer Anwendung sein kann, das auf eine Bedingung in Bezug auf die Anwendung achtet, oder ein Gerät wie ein IoT-Sensor, der ein Event erzeugt. Ein Stream wird manchmal als Pfeil – von links nach rechts – dargestellt, der mit dem Observer-Prozess beginnt und durch einen oder mehrere Handler fließt, bis er vollständig verarbeitet ist, in einem Fehlerstatus endet oder sich in abgeleitete Streams gabelt. Bei der reaktiven Programmierung geht es darum, diese Observer und Handler zu erstellen und den Stream je nach Bedarf mit Threads zu versehen.

Geräte-generierte Streams sind leicht zu verstehen. Aber Streams, die von durch Software eingefügten Observern erzeugt werden, sind komplizierter. Normalerweise arbeiten diese Elemente entweder mit dem fertigen Prozess einer Anwendung oder sie laufen periodisch, um ein Datenbankelement zu überwachen. Wenn dieses Softwareelement eine Bedingung erkennt, erzeugt es ein Event im Stream.

Ein Event Stream wird entweder von den Handlern selbst gesteuert, wo die Arbeit an einen bestimmten nächsten Prozess weitergeleitet wird, oder von einem Message Bus, wie zum Beispiel einem Enterprise Service Bus (ESB) oder einer Nachrichtenwarteschlange, die die Nachricht an bestimmte Bus Listener weiterleitet. Der Nachrichtenverarbeitungsprozess bestimmt, ob eine Nachricht an mehrere Handler oder an einen einzelnen Handler gesendet wird, und er ist normalerweise auch für den Lastausgleich zwischen mehreren parallelen Handlern oder für die Bereitstellung von Ersatz-Handlern im Falle eines Fehlers verantwortlich.

Jeder Handler muss entweder die Nachricht weiterleiten, feststellen, dass der Stream-Prozess beendet ist und die Nachricht löschen, oder einen Fehler erzeugen. Der Handler kann entscheiden, ob er eine Nachricht an mehrere Streams gabelt (fork) oder einen neuen Stream oder mehrere Streams erzeugt. Diese Fork-Bedingungen werden oft verwendet, um Aufgaben in der Nachrichtenverarbeitung zu trennen. Eine Nachricht kann sowohl eine lokale Antwort zum Öffnen eines Gate als auch eine Nachricht an ein Transaktionsverarbeitungssystem erzeugen.

In The Reactive Principle, dem Nachfolger von The Reactive Manifesto, definieren Jonas Bonér et al. die acht Prinzipien, die eine Anwendung verkörpern muss, um als reaktiv zu gelten:

  1. Reaktionsschnell bleiben. Reagieren Sie immer zeitnah und rechtzeitig.
  2. Unsicherheit akzeptieren. Bauen Sie Zuverlässigkeit trotz unzuverlässiger Grundlagen auf.
  3. Fehler akzeptieren. Rechnen Sie damit, dass etwas schief geht, und bauen Sie Widerstandsfähigkeit auf.
  4. Autonomie sichern. Entwickeln Sie Komponenten, die unabhängig voneinander agieren und zusammenarbeiten.
  5. Maßgeschneiderte Konsistenz. Individualisieren Sie die Konsistenz pro Komponente, um ein Gleichgewicht zwischen Verfügbarkeit und Leistung herzustellen.
  6. Zeit entkoppeln. Verarbeiten Sie asynchron, um Koordination und Wartezeiten zu vermeiden.
  7. Raum entkoppeln. Schaffen Sie Flexibilität, indem Sie das Netzwerk einbeziehen.
  8. Dynamik handhaben. Passen Sie sich kontinuierlich an wechselnde Anforderungen und Ressourcen an.
Acht Prinzipien reaktiver Programmierung
Abbildung 1: Die acht Prinzipien reaktiver Programmierung.

Vorteile und Herausforderungen der reaktiven Programmierung

Die primären Vorteile reaktiver Programmiertechniken sind ihre Fähigkeit,:

  • eine bessere Kontrolle über die mit der Verarbeitung von Events verbundenen Reaktionszeiten zu bieten;
  • Konsistenz im Softwaredesign für Echtzeitsysteme zu ermöglichen, um Entwicklungs- und Wartungskosten und -aufwand zu reduzieren
  • Lastausgleich und Ausfallsicherheit zu unterstützen, um die Qualität der Erfahrung zu verbessern; und
  • das Konzept eines Streams oder Ereignisflusses explizit zu machen und so die Verwaltung von Rechenelementen und Verarbeitungsressourcen insgesamt zu verbessern, indem sie visueller gemacht werden.

Diese Vorteile bringen Herausforderungen mit sich, darunter:

  • Das Hinzufügen von Observer-Prozessen zu aktueller Software kann schwierig oder unmöglich sein, abhängig von der Verfügbarkeit von Quellcode und den Programmierkenntnissen der Mitarbeiter.
  • Reaktives Design ist eine große Umstellung für Entwickler, und die Bemühungen werden eine Lernkurve aufweisen, während der mehr Validierung und Überwachung von Design und Kodierung erforderlich ist.
  • Reaktive Systeme können durch eine übermäßige Anzahl von Prozessen, die mit dem Stream verbunden sind, leicht Verzögerungen anhäufen.

Reaktive Programmierung einführen

Gute reaktive Programme beginnen mit einem klaren Diagramm des Event Streams, das alle spezifischen Handler-Prozesse und ihre Rolle bei der Verarbeitung, Beendigung oder Fehlererzeugung enthält. Danach wird die Hosting-Plattform – Edge, Cloud oder Rechenzentrum – ausgewählt und im Stream-Diagramm für jeden Prozess festgelegt, um ein Hin und Her über die Grenzen der Hosting-Plattform hinweg zu vermeiden. Dann kann die Entwicklung beginnen.

Die folgenden Best Practices sollten während der Entwicklung beachtet werden:

  • Wenn ein Event Stream eine reale Reaktion auslösen muss, zum Beispiel das Öffnen eines Tors, halten Sie die Control Loop kurz, indem Sie den reagierenden Prozess näher an den Anfang des Stroms verschieben und ihn in der Nähe der Ereignisquelle hosten.
  • Vermeiden Sie die Verwendung von Programmiersprachen und -techniken, die zustandsbehaftete Komponenten erstellen, die Daten mit der Software speichern, um sicherzustellen, dass die Komponenten einfach skaliert und ersetzt werden können.
  • Überprüfen Sie den Speicherort und die Implementierung von Datenbanken, die von einem der Handler-Prozesse benötigt werden, um sicherzustellen, dass der Datenbankzugriff keine zusätzlichen Latenzzeiten verursacht oder Cloud-Grenzen überschreitet, was zu zusätzlichen Kosten führt.
  • Verweisen Sie bei jedem Entwicklungsschritt auf das Event-Stream-Diagramm, um sicherzustellen, dass es gepflegt, aktuell und genau ist.

Anwendungsfälle für reaktive Programmierung

Die primären Anwendungsfälle für reaktive Programmierung sind:

  • IoT-Anwendungen, bei denen Sensoren Events erzeugen, die dann Prozessschritte in der realen Welt steuern, Geschäftstransaktionen erzeugen oder beides. Dies ist die am schnellsten wachsende Anwendung reaktiver Programmiertechniken, wenn auch nicht das traditionelle Ziel.
  • Anwendungen, die Statusinformationen von Netzwerken oder Datenverarbeitungselementen durch eingefügte Softwareagenten sammeln, die Aktivitäten oder Datenelemente überwachen. Dies ist die erste klassische Anwendung der reaktiven Programmierung, aber eine, die mit dem IoT konvergiert.
  • Jede Anwendung, die eine hochgradig interaktive Bedienung von Benutzer zu Benutzer erfordert, insbesondere wenn jeder Tastendruck verarbeitet und interpretiert werden muss. Dies ist die andere klassische Anwendung der reaktiven Programmierung, zu der jetzt auch Spiele und einige Social-Media-Anwendungen gehören.
  • Signalisierung zwischen Anwendungen, insbesondere zwischen so genannten Vordergrund-Anwendungen und Hintergrund- oder Batch-Anwendungen, die statistische Analysen und Datenbankbereinigungen durchführen. Dieser Anwendungsfall beinhaltet normalerweise einen Daemon-Prozess, der Änderungen überwacht und einen Event Stream aktiviert, wenn eine solche erkannt wird.
  • Koordination zwischen funktionaler AWS Lambda Cloud-Verarbeitung und Backend-Rechenzentrumsverarbeitung, wobei ein Event die Ausführung eines Backend-Prozesses auslöst. Dies erleichtert einige Formen der Hybrid-Cloud-Entwicklung.
Diese Definition wurde zuletzt im Mai 2021 aktualisiert

Erfahren Sie mehr über Softwareentwicklung

ComputerWeekly.de
Close