Development

Kolumne: Stropek as a Service

WebSockets als interessante Alternative für Backend-for-Frontend
Keine Kommentare

Viele Teams, mit denen ich in SaaS-Architekturprojekten zusammenarbeite, kommen auf die Idee, ein allgemeingültiges HTTP-basierendes Web-API zu entwickeln, das von allen Frontends (z. B. Mobile, Web) sowie von externen Partnern verwendet wird. Diese Vorgehensweise hat allerdings nicht nur Vorteile.

Auf den ersten Blick liegen die Vorteile dieser Vorgehensweise auf der Hand. Man entwickelt das API nur einmal, im Sinne von Dogfooding mutet man das API nicht nur Partnern zu, sondern verwendet es auch selbst, so soll es zu keinen Inkonsistenzen kommen, die bei mehreren getrennten APIs kaum vermeidbar wären, u.v.m.

Allgemeingültige APIs werden zum Problem

Ein API zu entwickeln, das für jeden Anwendungsfall gut passt, erweist sich in der Praxis aber als schwierig. Externe Nutzer des API haben mit ziemlicher Sicherheit andere Anforderungen als die verschiedenen internen Frontends. Während nach außen Datenaustauschprozesse und höherwertige Geschäftstransaktionen (z. B. Bestellung, Preisauskunft) im Vordergrund stehen, braucht das Frontend ein API, dessen Struktur den Aufbau und die Logik der Benutzerschnittstelle widerspiegelt. Ist das nicht der Fall, ist das Frontend nicht selten gezwungen, die Daten mit dem Backend über nicht perfekt zum Anwendungsfall passende Web-APIs auszutauschen. Die Folgen sind unnötig schlechte Performance durch zu viele API Requests oder vermeidbar große übertragene Datenmengen.

Backend-for-Frontend-Ansatz

Ein Lösungsansatz für diese Herausforderung wird als Backend-for-Frontend-Pattern bezeichnet. Anstatt eines allgemeinen API für alle Benutzerkreise, werden spezifische APIs entwickelt, die als Backend für spezifische Frontends dienen. Sie sind genau auf deren Bedürfnisse zugeschnitten. Um Synergien weiterhin nutzen zu können und Inkonsistenzen zu vermeiden, spricht nichts dagegen, im Hintergrund auf allgemeinere APIs zuzugreifen.

Ein weiterer Vorteil dieses Ansatzes ist, dass die Agilität gesteigert wird. Frontends ändern sich in der Regel rascher als Backend APIs, da die Frontends „Modetrends“ unterworfen sind während das Backend durch die sich langsamer ändernden Geschäftsprozesse und -modelle getrieben wird. Ändert man die Logik des Frontends, werden oft geänderte Backend APIs notwendig. Ist man auf ein allgemeines Web-API angewiesen, auf das auch externe Partner zugreifen, ist man bei Änderungen zurückhaltend, weil Fehler gravierendere Auswirkungen haben können. Breaking Changes sind sowieso tabu. Wenn ein Frontend sein eigenes, spezifisches Backend hat, das nicht für die Verwendung durch Dritte freigegeben ist, sind Änderungen viel einfacher. Oft ist ein Team für Frontend und zugehöriges Backend verantwortlich. Die Kommunikationswege sind dadurch kurz, Abstimmung ist einfach, das Reagieren auf geänderte Anforderungen ist schnell möglich.

WebSockets statt Request/Response

Bei APIs, die externen Partnern zur Verfügung gestellt werden, sind HTTP-basierende Ansätze wie REST oder OData der De-facto-Industriestandard. Das Request/Response-Pattern von HTTP passt gut zu den Anforderungen für den Datenaustausch zwischen Organisationen. Asynchrone Rückmeldungen können ebenfalls über HTTP abgewickelt werden (z. B. WebHooks).

Wenn man aber ein spezielles Backend für ein gewisses Frontend baut, ist man freier hinsichtlich der Kommunikationsstandards. WebSockets ist ein attraktiver Kandidat dafür. Der Standard ist nicht mehr ganz neu, es gibt ihn seit 2011, dementsprechend gut ist die Unterstützung in Browsern. Laut Can I use haben rund 93 Prozent aller Internetbenutzer einen Browser, der WebSockets unterstützt. Nur wer sich mit steinalten Browsern wie IE > 10 herumplagen muss, bekommt Probleme.

Beim WebSockets-Protokoll fragt der Client beim Server via HTTP ein sogenanntes Upgrade des Protokolls von HTTP auf WebSockets an. Wenn der Server damit einverstanden ist, antwortet er mit dem HTTP-Statuscode 101 Switching Protocols (Abb. 1).

 

Abb. 1: WebSockets-Verbindungsaufbau

Abb. 1: WebSockets-Verbindungsaufbau

 

Ab diesem Zeitpunkt kann die bestehende TCP/IP-Verbindung zwischen Client und Server für den bidirektionalen Austausch von Text- oder Binärdaten verwendet werden (Abb. 2). Im Gegensatz zu HTTP kann der Server also dem Client aktiv Nachrichten schicken, um ihn so über Events (z. B. Fertigstellung eines länger laufenden Prozesses) zu informieren.

 

Abb. 2: WebSockets-Datenaustausch in Chrome Developer Tools

Abb. 2: WebSockets-Datenaustausch in Chrome Developer Tools

 

WebSockets hat den Ruf, dass das Protokoll hauptsächlich für Chatlösungen oder Onlinespiele hilfreich ist. Meiner Ansicht nach ist der mögliche Einsatzbereich aber viel größer. Gerade bei Geschäftsanwendungen in der Cloud kommt es oft vor, dass länger laufende Prozesse wie Datenimport, Berichterstellung und die Abwicklung von komplexen Geschäftstransaktionen asynchron implementiert werden. Bei ihrer Verarbeitung werden Nachrichten oder Events ausgetauscht (z. B. über Azure Service Bus oder Azure Event Grid), Workflows abgearbeitet (z. B. über Azure Logic Apps) oder asynchrone Serverless Functions (z. B. in Azure Functions) ausgeführt. Am Ende muss man das Frontend über das Ergebnis der Verarbeitung informieren. Macht man das über HTTP, bekommt man nicht selten Probleme mit Timeouts oder muss Performancenachteile (z. B. beim Polling) in Kauf nehmen. Durch WebSockets wird die Sache einfacher.

Frameworks sind hilfreich

Wenn man dem Konzept Backend for Frontend folgt, ist es denkbar, dass man eine Kombination von Technologien auf Server und Client einsetzt, die nicht auf möglichst allgemeine Einsetzbarkeit, sondern auf einfaches Zusammenspiel optimiert ist. Das spielt besonders bei WebSockets eine große Rolle. Ohne Framework ist das WebSockets-Protokoll recht simpel. Beide Seiten senden und empfangen Nachrichten, sonst nichts. Höherwertige Funktionen wie Remote Procedure Calls (RPC) über WebSockets-Verbindung gibt es nicht. Dafür braucht man Frameworks, die auf WebSockets aufbauen. Das prominenteste Beispiel im Microsoft-Umfeld ist SignalR. Es kommt mit APIs für .NET, .NET Core, Java und JavaScript.

Ich würde kein öffentliches API für eine SaaS-Lösung auf SignalR aufbauen. Damit würde man die Technologien zu sehr einschränken und Partner, die andere Plattformen verwenden, möglicherweise ausschließen. Außerdem fehlen bei WebSockets und SignalR wertvolle Metadatenstandards wie Open API (aka Swagger), mit denen plattform- und technologieunabhängig APIs beschrieben und Client-Libraries generiert werden könnten.

Bei einem Backend-for-Frontend-Ansatz, bei dem das Backend- und das Frontend-Team organisatorisch eine gewisse Verbindung haben, ist eine solche Einschränkung aber angesichts der funktionalen Möglichkeiten und hohen Entwicklungsproduktivität eine Überlegung wert.

Herausforderungen bei Einsatz von WebSockets

Wer WebSockets in größerem Stil nutzen möchte, hat auch ein paar Herausforderungen zu lösen. Die Kernidee von WebSockets ist eine Duplexverbindung zwischen Client und Server. Es liegt daher in der Natur der Sache, dass die Verbindung im Gegensatz zu HTTP Stateful ist. Während es bei HTTP problemlos möglich ist, dass ein Response von einem und der nächste Response von einem anderen Webserver kommt, bleibt bei WebSockets eine Verbindung aufrecht und bindet den Client an einen bestimmten Server. Diese Tatsache macht den Betrieb von WebSockets-Lösungen in Clustern schwieriger, als das bei HTTP der Fall ist.

Auch das Skalieren über einen einzelnen Server hinaus ist nicht einfach. Jede WebSocket-Verbindung belegt Ressourcen auf dem Server. Wenn man es mit vielen Benutzern zu tun hat, muss man sich Gedanken darüber machen, wie man die Serverinfrastruktur elastisch an sich ändernde Verbindungszahlen anpasst. Darüber hinaus braucht man auf der Serverseite eine Messaging-Infrastruktur, damit man Nachrichten an bestimmte Clients weiterleiten kann. Schließlich sind die Clientverbindungen über viele Server verteilt. Wenn ein Event über WebSockets an einen bestimmten Client geschickt werden soll, muss man den Server finden, zu dem der Client gerade eine bestehende Verbindung hat.

Azure-Cloud bietet Lösungen

Zum Glück bietet Microsoft in der Azure-Cloud PaaS- und Serverless-Dienste an, die die oben genannten Probleme lösen. Entwicklungsteams können sich dadurch ganz auf die Anwendungslogik konzentrieren und die Infrastruktur die Sorge von Microsoft sein lassen. Hier eine Liste wichtiger Azure-Dienste für WebSockets bzw. SignalR:

  • Azure App Service, der wichtigste Azure PaaS und Serverless Service zum Betrieb von Webanwendungen und Web-APIs, unterstützt WebSockets seit Jahren.
  • Azure bietet eine Vielzahl von Möglichkeiten, wie die Server eines Clusters untereinander kommunizieren können. Drei der beliebtesten Lösungen sind Azure Service Bus, Azure Event Grid und Azure Cache for Redis.
  • Der neueste Baustein in Sachen WebSockets in Azure löst ein bis vor kurzem noch ungelöstes Problem: Skalierbarkeit. Der Azure SignalR Service befreit SignalR-Anwendungen vom Verwalten einer großen Anzahl von WebSocket-Verbindungen. Clients verbinden sich mit dem Azure SignalR Service. Die Anwendungsserver haben nur fünf WebSockets-Verbindungen zum Azure SignalR Service offen. Die Nachrichten von und zu den Clients werden über diese fünf Verbindungen über Multiplexing verteilt.

Blazor goes WebSockets

Ein schönes Fallbeispiel, wie Microsoft selbst SignalR verwendet, ist Blazor. Das von vielen sehnlich erwartete Framework verspricht Single Page Apps geschrieben mit C# und Razor im Browser, und das ohne Plug-ins auf Basis von WebAssembly. Da WebAssembly allerdings noch in den Kinderschuhen steckt, müssen wir etwas Geduld haben, bis C# im Browser läuft.

Damit Entwicklungsteams schon jetzt von den Entwicklungen im Blazor-Projekt profitieren können, bringt Microsoft die sogenannten Razor Components heraus. Es handelt sich dabei um Blazor, nur dass der C#-Code am Server statt am Client läuft. Jede DOM-Interaktion (Änderung oder Event-Behandlung) wird über SignalR an den Server geschickt und dort verarbeitet. Es ist also eine Single Page App, bei der die View Logic am Server läuft und mit der View über WebSockets verbunden ist. Da in dieser Konstellation keine Preview-Komponenten wie WebAssembly notwendig sind, wird Microsoft Razor Components schon mit .NET Core 3 liefern.

Die Codeänderungen, die für den Wechsel von rein clientseitiger Logik mit WebAssembly zu Verteilung der Logik auf Client und Server mit SignalR notwendig sind, sind minimal. Das verspricht Flexibilität, um das Deployment-Modell an verschiedene Anforderungsszenarien ohne große Änderungen im Code anpassen zu können.

Fazit

WebSockets sind kein genereller Ersatz für HTTP-basierende Web-APIs. Backends, die aber speziell für ein Frontend gemacht sind, können von WebSockets und darauf aufbauenden Frameworks wie SignalR profitieren. In der Vergangenheit haben sich Teams oft angesichts der Schwierigkeiten in Hinblick auf Clusterfähigkeit und Skalierbarkeit abschrecken lassen. In der Azure-Cloud sind aber in den letzten Monaten einige Dienste dazugekommen, die die Spielregeln verändern und SignalR auch in Projekten mit kleinem Entwicklungs- und Betriebsbudget möglich machen.

Unsere Redaktion empfiehlt:

Relevante Beiträge

Hinterlasse einen Kommentar

Hinterlasse den ersten Kommentar!

avatar
400
  Subscribe  
Benachrichtige mich zu:
X
- Gib Deinen Standort ein -
- or -