Amazon Web Services (AWS) bietet eine Fülle an Services, die für Problemlösungen in den verschiedensten Anwendungsgebieten genutzt werden können.
Darunter befinden sich auch Dienste, durch die eine Entkopplung von Anwendungen erreicht werden kann.
Dadurch können Abhängigkeiten einzelner Komponenten innerhalb der Applikation minimiert und eine generell höhere Widerstandsfähigkeit erreicht werden.
Vor allem im Bereich verteilter microservice-orientierter bzw. serverloser Applikationen erfreuen sich Dienste wie „Amazon Simple Queue Service“ (SQS) oder „Amazon Simple Notification Service“ (SNS) großer Beliebtheit.
Nun stellt sich jedoch folglich die Frage: Warum sollten diese Services überhaupt genutzt werden, um verteilte Anwendungen zu entkoppeln?
Hierzu ein einfaches Beispiel: Ein klassischer, synchroner Aufbau einer serverlosen Anwendung besteht meist aus einer API, einem Datenverarbeitungsservice und weiteren Diensten wie z.B. Storage oder Datenbanken. Im vorliegenden Beispiel werden zwei gängige (serverlose) AWS Services genutzt:
Bezeichnung | Service |
---|---|
API (Application Programming Interface) | Amazon API Gateway |
Compute/Verarbeitung | AWS Lambda |
Ein einfacher Architekturentwurf mit den genannten Services könnte folgendermaßen aussehen:
Die API schickt in diesem Beispiel eine Anfrage mit entsprechenden Übergabeparametern an den Datenverarbeitungsservice. Dieser erfüllt im Folgenden eine definierte Aufgabe, z.B. das Anlegen eines Datenbankeintrags und schickt eine Rückmeldung an die API, welche den User bzw. den anfragenden Service in Kenntnis setzen kann.
Nun könnte man in erster Überlegung bei dieser Umsetzung keinerlei Probleme sehen, sollten alle Komponenten ihren vordefinierten Zweck fehlerfrei erfüllen. Bei genauerer Betrachtung fällt jedoch auf, dass eine implizite Abhängigkeit zwischen allen Komponenten der Architektur besteht. Die API ist auf eine erfolgreiche Bearbeitung der Anfrage in AWS Lambda angewiesen. Lambda wiederum ist davon abhängig, dass die weiteren Verarbeitungen der Applikation wie das Anlegen eines Datenbankeintrags oder andere (Backend-)Operationen den Vorgaben entsprechend ausgeführt werden.
Da die Daten in diesem Beispiel nicht persistent zwischen den einzelnen Diensten gespeichert werden, führt ein Ausfall einer Komponente dazu, dass diese Informationen verloren gehen. Folglich muss bei einer nicht erfolgreichen Bearbeitung der anfragende Service bzw. Nutzer in Kenntnis gesetzt werden, um eine wiederholte Verarbeitung anstoßen zu können.
Wie in der Einführung bereits erläutert, bietet AWS Services, die bei diesem Problem Abhilfe schaffen können und eine Entkopplung bzw. asynchrone Ausrichtung der Anwendung erreichen sollen. Im Folgenden werden zwei dieser Services, „Simple Queue Service“ und „Simple Notification Service“, genauer betrachtet.
SQS ist ein Queue-Service, der es ermöglicht, Nachrichten persistent zwischen verschiedenen Verarbeitungsschritten zu speichern. Hierbei legen sog. Producer, die zu verarbeitenden Messages in einer Warteschlange ab, welche dann wiederum von Consumern bearbeitet werden können. Nach erfolgreicher Verarbeitung löscht der Consumer die Nachricht aus der Queue, sodass eine Anfrage nicht mehrfach konsumiert wird.
Sollte die Abarbeitung einer Anfrage scheitern, muss der gesamte Prozess jedoch nicht wie zuvor gesamtheitlich neu gestartet werden, sondern kann von einem anderen Consumer wiederholt werden, bis ein gewünschtes Ergebnis erzielt worden ist.
SNS hingegen nutzt ein sog. PubSub (Publish/Subscribe) Modell, mit dem eine Nachricht an fast beliebig viele Endpunkte verteilt werden kann.
Jeder Endpunkt erhält dabei eine identische Kopie der Nachricht, um diese verarbeiten zu können.
Dazu werden sog. Topics verwendet, welche eine Gruppierung von Endpunkten darstellen, die alle Zugriffe auf die gleichen Nachrichten benötigen. Die Endpunkte wiederum werden als Subscriber bezeichnet, die über Nachrichten in dem zugehörigen Topic in Kenntnis gesetzt werden.
Mögliche Endpunkte:
Das folgende Beispiel soll diese Thematik verdeutlichen:
Der Publisher (bspw. eine API) veröffentlicht eine Nachricht im User-Registration-Topic (bspw. Anmeldung eines neuen Users). Die beiden Subscriber Create DB Entry und Validation E-Mail erhalten folglich die Nachricht, die im Topic hinterlegt worden ist (die zugehörigen User-Daten). Diese Subscriber können dann ihren zugewiesenen Aufgaben nachgehen und die Nachricht entsprechend verarbeiten. (Daher auch die Bezeichnung PubSub)
Über die sog. Retry-Policy kann darüber hinaus definiert werden, wie oft und in welchen Intervallen Nachrichten erneut an einen Subscriber übergeben werden, sollte die Zustellung nicht in einem positiven Ergebnis münden. Das heißt, Nachrichten werden bei SNS ebenfalls im Backend gehalten, bis eine erfolgreiche Zustellung garantiert worden ist.
Ein ausschlaggebender Unterschied zwischen SQS und SNS besteht in der Zustellung der Nachrichten. Typischerweise wird eine Message bei SQS von einem Consumer bearbeitet, dagegen erhalten alle Subscriber eines Topics bei SNS eine identische Kopie der Nachricht, man spricht hier auch von einem sog. fan out (auffächern).
Ebenfalls kann unterschieden werden, wie Consumer/Subscriber über Events in beiden Services informiert werden. SQS nutzt polling, d.h. verarbeitende Dienste müssen die Queue abfragen, um Events erhalten zu können, wohingegen alle Subscriber eines Topics in SNS automatisch die für sie relevanten Nachrichten erhalten.
Man nutzt beide Services also für unterschiedliche Anwendungsfälle, erreicht aber sowohl mit SQS als auch mit SNS eine Entkopplung seiner Anwendungen.
Natürlich existieren noch weitere Unterschiede zwischen diesen beiden Diensten, jedoch würde dies für eine einfache Einführung zu weit ins Detail gehen. Für mehr Informationen rund um diese beiden Services empfiehlt sich ein Blick in die Dokumentation:
Eine überarbeiteter, entkoppelter Architekturansatz mit SQS würde bspw. wie folgt aussehen:
Einkommende Anfragen der API werden in diesem Ansatz nicht mehr direkt an Lambda übergeben, sondern automatisch in der Queue abgelegt. Die API kann außerdem nach erfolgreicher Übergabe an SQS dem anfragenden Service/Nutzer unmittelbar eine Rückmeldung senden. Ein Warten auf die gesamte Verarbeitung ist hier im Gegensatz zum ersten Ansatz also ebenfalls nicht mehr notwendig.
AWS Lambda kann sich dann die zu verarbeitende Nachricht selbstständig aus der Queue holen und deren Bearbeitung vollziehen.
Diese Abarbeitung geschieht dann völlig losgelöst (asynchron) im Hintergrund.
Ein resultierender Architekturansatz mit SNS könnte folgendermaßen aussehen:
Hierbei werden die Anfragen über die API in einem zugehörigen SNS-Topic veröffentlicht und alle zugehörigen Subscriber benachrichtigt. Wie bei der Nutzung von SQS kann auch hier unmittelbar nach Übergabe der Nachricht an SNS eine Rückmeldung an den aufrufenden Service/Nutzer gesandt werden. Im Gegensatz zu SQS müssen sich hier jedoch die Subscriber/Consumer die Nachrichten nicht selbstständig aus dem zugehörigen Topic beschaffen, sondern werden, wie zuvor kurz erläutert, informiert bzw. erhalten die veröffentlichte Nachricht automatisch.
Auch hier wird die Verarbeitung der Nachricht losgelöst (asynchron) im Hintergrund ausgeführt.
Um den eingeführten Ansatz in die Praxis umsetzen zu können, müssen folgende Ressourcen erzeugt werden:
1. SQS-Queue
Es bietet sich an, zunächst eine SQS-Queue anzulegen, welche dann für die persistente Zwischenspeicherung der Nachrichten genutzt werden soll.
2. IAM Rolle & Policy
Damit die zu erstellende API die nötigen Berechtigungen erhält, um Nachrichten in der Queue hinterlegen zu können, muss eine IAM Policy mit den zugehörigen Rechten ausgestattet werden. Diese Policy wiederum wird an eine IAM Rolle angefügt, welche dann von der API genutzt werden kann.
3. HTTP/REST-API
Die API benötigt eine POST-Route/Methode, über welche einkommenden Nachrichten in der Queue abgelegt werden können. Beim Anlegen dieser kann über eine sog. Service-Integration der Endpunkt direkt mit der zuvor erzeugten Queue verbunden werden. Hierbei muss außerdem die erstellte Rolle referenziert werden, um dem API-Endpunkt den Zugriff auf die SQS-Queue zu ermöglichen.
4. Lambda-Funktion
Um die Anfrage weiterzuverarbeiten, kann eine Lambda-Funktion erstellt werden. Hierbei wird über einen sog. Lambda-Trigger die Funktion mit der Queue verbunden. Anschließend kann die erstellte Funktion die Queue pollen (abfragen), um eine Nachricht aus dieser auslesen und bearbeiten zu können.
Hinweise:
Um zu überprüfen, ob alle Komponenten ihren vorgesehenen Zweck erfüllen, wird folgender POST-Request mithilfe von Postman an die API gesendet:
{
"message": "Hello, world!"
}
Sollte die Ablage der Anfrage in der Queue erfolgreich sein, impliziert die API über den Status-Code 200 eine erfolgreiche Übergabe der Nachricht. Außerdem wird ein JSON-Object zurückgegeben, welches u.a. folgendes Attribut besitzt:
"MessageId": "720bb173-9ae3-4cc6-8e06-fd0dc55555c6"
Zu guter Letzt werden die CloudWatch-Logs der Lambda-Funktion geprüft, um sicherzustellen, dass die Anfrage den Vorgaben entsprechend angekommen ist.
Das JSON-Object, welches in der Funktion ausgegeben wird, enthält neben zusätzlichen Informationen die folgenden beiden Attribute:
{
messageId: '720bb173-9ae3-4cc6-8e06-fd0dc55555c6',
...
body: '{"message": "Hello, world!"}'
}
Somit kann sichergestellt werden, dass die Integration der API mit SQS funktioniert und der zusätzlich erstellte Lambda-Trigger ebenfalls seine Aufgabe erfüllt.
Um die Anwendung mit SNS entkoppeln zu können, werden folgende Ressourcen erstellt:
1. SNS-Topic
Zunächst wird ein SNS-Topic erstellt, in welchem die API Nachrichten veröffentlichen kann. Außerdem können direkt Subscriber hinzugefügt werden, sollten diese bereits existieren.
2. IAM Rolle & Policy
Auch hier müssen eine Rolle bzw. Policy mit den benötigten Berechtigungen zum Veröffentlichen von Nachrichten in dem bereits erstellten Topic definiert werden.
3. REST-API
Bei der Verbindung der API mit SNS wird ebenfalls eine POST-Methode benötigt. Über die Service-Integration wird der Endpunkt dann mit SNS verknüpft und die erstellte Rolle entsprechend referenziert.
4. Subscriber
Im Gegensatz zum Ansatz mit SQS kann nun eine einzelne Nachricht an beliebig viele Subscriber zugestellt werden. Dazu kann im zugehörigen SNS-Topic aus einer Reihe von Subscriber-Typen ausgewählt werden. Zu Demonstrationszwecken wurde sich sowohl für eine E-Mail Zustellung als auch eine Lambda-Integration entschieden.
Hinweise:
Auch hier wird ein POST-Request mit folgendem Inhalt via Postman an den mit SNS verknüpften Endpunkt gesendet:
{
"message": "Hello, world!"
}
Die API sendet bei positiver Zustellung ein JSON-Object mit u.a. folgender MessageId:
"MessageId": "5afc5dec-47ef-503d-ad76-06ca6aa0a5fa"
In den Cloudwatch-Logs der zugehörigen Lambda-Funktion kann ebenfalls beobachtet werden, dass die Ausgabe die Attribute
{
messageId: '5afc5dec-47ef-503d-ad76-06ca6aa0a5fa',
...
message: 'Hello, world!'
}
enthält.
Außerdem wurde die Nachricht an die zugehörige E-Mail gesandt:
Mit SQS und SNS können serverlose Architekturen entkoppelt werden. Dadurch kann eine gewisse Zuverlässigkeit, Skalierbarkeit und Belastbarkeit innerhalb der Applikation garantiert werden. Durch die Entkopplung können Nachrichten von bspw. dem Frontend einer Web-App persistent im Backend gespeichert werden, bis eine erfolgreiche Bearbeitung garantiert werden kann. Hierfür eignen sich SQS, um eine Nachricht singulär weiterverarbeiten zu können bzw. SNS sollten mehrere Komponenten des Systems die gleiche Information benötigen. Außerdem können Entwickler durch die Nutzung dieser Dienste sehr genau steuern und überwachen, wie Nachrichten zwischen den einzelnen Bausteinen einer Anwendung weitergeleitet und verarbeitet werden.
Vielen Dank für das Lesen des Blog-Posts.
Sollten Sie weitere Fragen zum Entwickeln verteilter Anwendungen haben oder sich für den Bereich des Serverless-Computing interessieren, stehe ich für Anfragen jederzeit gerne zur Verfügung. Ebenfalls ist Feedback aller Art zu diesem Blog-Post gerne gesehen.
E-Mail: info@evoila.de