Softwarearchitektur verstehen und entscheiden

Aaron KreisÜber mich
Skalierung

Stateless Services

Dienste ohne gespeicherten Sitzungszustand

Die Idee und ihr Nutzen

Ein Kunde öffnet im Schadenportal seiner Versicherung die Akte zu Fall 4711 und klickt danach auf „Dokumente zu diesem Schaden“. Das Schadenportal läuft gleichzeitig auf zwei Servern, auf beiden dasselbe Programm. Jedes der beiden laufenden Exemplare heißt Instanz, hier Instanz 1 und Instanz 2. Jede Anfrage geht zuerst an einen Lastverteiler, ein Programm, das die Anfrage an die gerade am wenigsten beschäftigte Instanz weiterschickt. So bearbeitet vielleicht Instanz 1 die erste Anfrage des Kunden und Instanz 2 die zweite. Damit der Kunde vom Wechsel nichts bemerkt, muss Instanz 2 alles erfahren können, was Instanz 1 festgestellt hat, zum Beispiel, dass der Kunde angemeldet ist. Solche Angaben schickt der Browser deshalb mit jeder Anfrage mit, oder die Instanzen legen sie in einem gemeinsamen Speicher ab, den beide Instanzen lesen können. Ein Dienst, dessen Instanzen so arbeiten, heißt zustandsloser Dienst, englisch Stateless Service. Der Name meint: Keine Instanz behält den Zustand nur im eigenen Arbeitsspeicher. Zustand heißen alle Angaben, die über eine einzelne Anfrage hinaus gebraucht werden, etwa der Anmeldestatus.

Bausteine: Kunde: öffnet Fall 4711, klickt weiter; Lastverteiler: schickt jede Anfrage an eine beliebige Instanz; Instanz 1: merkt sich nichts Eigenes; Instanz 2: merkt sich nichts Eigenes; Sitzungsspeicher: Anmeldestatus; Fachdatenbank: Schadenakten. Verbindungen: Kunde — Lastverteiler; Lastverteiler — Instanz 1; Lastverteiler — Instanz 2; Instanz 1 — Sitzungsspeicher; Instanz 2 — Fachdatenbank; Instanz 1 — Fachdatenbank; Instanz 2 — Sitzungsspeicher. Anmeldestatus und Schadenakten liegen in gemeinsamen Speichern, deshalb kann jede Instanz jede Anfrage bearbeiten.

Wie funktioniert es?

Der Kunde öffnet im Schadenportal Fall 4711. Sein Browser schickt mit der Anfrage die Fallnummer 4711 und einen Anmeldenachweis. Der Anmeldenachweis ist eine Angabe, an der das Schadenportal erkennt, dass sich dieser Kunde angemeldet hat. Der Browser schickt den Anmeldenachweis bei jeder Anfrage mit. Der Lastverteiler schickt die erste Anfrage an Instanz 1. Instanz 1 lädt die Akte 4711 aus der Fachdatenbank, in der die Schadenakten liegen. Solange der Kunde angemeldet ist, hat er eine laufende Sitzung. Was das Schadenportal über diese Sitzung wissen muss, heißt Sitzungsstand. Dazu gehört zum Beispiel der Anmeldestatus des Kunden. Instanz 1 hält den Sitzungsstand im Sitzungsspeicher fest und nicht im eigenen Arbeitsspeicher. Den Sitzungsspeicher erreichen beide Instanzen. Instanz 1 zeigt dem Kunden die Akte. Danach klickt der Kunde auf „Dokumente zu diesem Schaden“. Weil Instanz 1 gerade stark beschäftigt ist, schickt der Lastverteiler diese zweite Anfrage an Instanz 2. Instanz 2 prüft den Anmeldenachweis. Braucht Instanz 2 weitere Angaben zur Sitzung, liest sie den Sitzungsstand, den Instanz 1 im Sitzungsspeicher hinterlegt hat. Die Dokumente lädt Instanz 2 aus der Fachdatenbank. Der Kunde sieht die Dokumentenseite und muss sich nicht neu anmelden.

Jede Instanz muss bei jeder Anfrage selbst herausfinden können, wer anfragt und welcher Sitzungsstand gilt. Ändert sich bei einer Anfrage etwas, das spätere Anfragen brauchen, speichert die bearbeitende Instanz es deshalb im Sitzungsspeicher oder in der Fachdatenbank und nie nur im eigenen Arbeitsspeicher. Den Arbeitsspeicher einer Instanz kann keine andere Instanz lesen. Zustandslos heißt nicht, dass das Schadenportal keine Daten speichert. Das Schadenportal liest und ändert Schadenakten in der Fachdatenbank.

Ist der Sitzungsspeicher nicht erreichbar, kann keine Instanz den Sitzungsstand lesen. Braucht das Schadenportal den Sitzungsstand, um die Anmeldung zu prüfen, scheitern dann die Anfragen auf beiden Instanzen zugleich.

Zustandslose Dienste: jede Instanz kann die nächste Anfrage bearbeiten

Beteiligte: Kunde: Browser schickt den Anmeldenachweis mit; Lastverteiler: verteilt frei; Instanz 1: merkt sich nichts Eigenes; Instanz 2: merkt sich nichts Eigenes; Sitzungsspeicher: gemeinsam für alle Instanzen; Fachdatenbank: Schadenakten. Ablauf: 1. Der Kunde öffnet Fall 4711. Sein Browser schickt mit der Anfrage den Anmeldenachweis und die Fallnummer. 2. Der Lastverteiler schickt die erste Anfrage an Instanz 1. 3. Instanz 1 lädt die Akte 4711 aus der Fachdatenbank. 4. Instanz 1 hält den Sitzungsstand im Sitzungsspeicher fest und nicht im eigenen Arbeitsspeicher. Beide Instanzen erreichen den Sitzungsspeicher. 5. Der Kunde sieht die Akte. 6. Der Kunde klickt auf „Dokumente zu diesem Schaden“. 7. Der Lastverteiler schickt die zweite Anfrage an Instanz 2. 8. Instanz 2 prüft den Anmeldenachweis. Braucht Instanz 2 weitere Angaben zur Sitzung, liest sie den Sitzungsstand aus dem Sitzungsspeicher. 9. Instanz 2 lädt die Dokumente aus der Fachdatenbank. 10. Der Kunde muss sich nicht neu anmelden. Fällt aber der Sitzungsspeicher aus, kann keine der beiden Instanzen den Sitzungsstand lesen.

Wann ist es sinnvoll und wann nicht?

Zustandslose Dienste lohnen sich

  • Der Lastverteiler soll jede Anfrage an die Instanz schicken können, die gerade am wenigsten zu tun hat. Eine stark beschäftigte Instanz 1 muss dann keine Folgeanfragen annehmen. Merkt sich Instanz 1 die Anmeldung dagegen im eigenen Arbeitsspeicher, findet Instanz 2 bei der nächsten Anfrage keine Anmeldung. Der Kunde wird dann aufgefordert, sich erneut anzumelden, obwohl er das gerade getan hat.
  • Bei steigender Last sollen weitere Instanzen starten. Eine neue Instanz kann vom ersten Moment an jede Anfrage bearbeiten, weil sie keinen Sitzungsstand von einer anderen Instanz übernehmen muss. Hielte jede Instanz den Sitzungsstand im eigenen Arbeitsspeicher, könnte eine neue Instanz nur Kunden bedienen, die sich neu anmelden.
  • Abgestürzte Instanzen und Instanzen, die für eine Aktualisierung neu starten, sollen sich jederzeit ersetzen lassen. Der Sitzungsstand liegt im Sitzungsspeicher und geht dabei nicht verloren, und kein Kunde muss sich neu anmelden. Läge der Sitzungsstand nur im Arbeitsspeicher von Instanz 1, wäre er nach einem Absturz oder Neustart von Instanz 1 verloren.

Zustandslose Dienste sind nicht das Richtige

  • Ein Programm hält einen großen Arbeitsstand im eigenen Arbeitsspeicher, zum Beispiel eine Berechnung, die über Stunden läuft. Zustandslos müsste das Programm den ganzen Arbeitsstand nach jedem Rechenschritt in einen gemeinsamen Speicher schreiben, damit eine andere Instanz weiterrechnen könnte. Dann läuft das Programm von Anfang bis Ende auf derselben Instanz und behält den Arbeitsstand im eigenen Arbeitsspeicher. Das Schreiben in einen gemeinsamen Speicher nach jedem Rechenschritt entfällt. Stürzt die Instanz ab, beginnt die Berechnung allerdings von vorn.
  • Eine Anwendung läuft nur auf einer einzigen Instanz, startet selten neu, und ein verlorener Sitzungsstand ist hinnehmbar. Es gibt dann keine zweite Instanz, die den Sitzungsstand aus einem gemeinsamen Speicher lesen müsste. Dann hält die Anwendung den Sitzungsstand im eigenen Arbeitsspeicher. Das Team, das die Anwendung betreibt, spart den Sitzungsspeicher, einen zusätzlichen Dienst, der installiert, überwacht und gegen Ausfall geschützt werden muss.
  • Eine vorhandene Anwendung hält ihren Sitzungsstand im Arbeitsspeicher und kann nicht umgebaut werden. Zustandslos würde die Anwendung erst durch einen Umbau, der den Sitzungsstand in einen gemeinsamen Speicher verlegt. Dann stellt das Team, das den Lastverteiler betreibt, am Lastverteiler Sticky Sessions ein: Der Lastverteiler schickt alle Anfragen eines Kunden an dieselbe Instanz. Der Umbau der Anwendung entfällt. Fällt diese Instanz aus, geht der Sitzungsstand allerdings verloren. Außerdem kann der Lastverteiler die Anfragen eines Kunden nicht mehr an eine weniger beschäftigte Instanz schicken.
  • Die Anwendung läuft auf wenigen Instanzen, und der Sitzungsstand ist klein. Dann kopiert jede Instanz den Sitzungsstand nach jeder Änderung auf die anderen Instanzen; das heißt Session Replication. Ein gemeinsamer Sitzungsspeicher, der betrieben und gegen Ausfall geschützt werden muss, entfällt. Bei vielen Instanzen oder großem Sitzungsstand müsste jede Instanz dagegen bei jeder Änderung viele Daten an viele andere Instanzen schicken.

Nachteile und typische Fehler

Der Sitzungsspeicher und die Fachdatenbank werden zu Bausteinen, von denen alle Instanzen abhängen. Beide brauchen einen eigenen Ausfallschutz, zum Beispiel einen zweiten Server mit einer Kopie der Daten, der bei einem Ausfall übernimmt. Bei jeder Anfrage muss die bearbeitende Instanz Angaben aus diesen beiden Speichern laden, und das dauert etwas länger als das Lesen aus dem eigenen Arbeitsspeicher. Das Team, das das Schadenportal entwickelt, muss für jede Angabe festlegen, wo sie liegt: in der Anfrage, im Sitzungsspeicher oder in der Fachdatenbank.

Typische Fehler und woran man sie erkennt:

  • Ausfall des Sitzungsspeichers: Der Sitzungsspeicher hat keinen eigenen Ausfallschutz und fällt aus. Keine Instanz kann dann den Anmeldestatus des Kunden lesen, und jede Instanz behandelt den Kunden als nicht angemeldet. Erkennbar ist das an Kunden, die auf allen Instanzen zugleich zur Anmeldung aufgefordert werden.
  • Versteckter Zustand im Arbeitsspeicher: Eine Instanz merkt sich doch etwas nur im eigenen Arbeitsspeicher, zum Beispiel die bisherigen Eingaben in einem Formular mit mehreren Seiten. Instanz 1 nimmt die erste Formularseite an und behält die Eingaben im eigenen Arbeitsspeicher. Der Lastverteiler schickt die zweite Formularseite an Instanz 2, und dort fehlen die Eingaben der ersten Seite. Erkennbar ist das an Fehlern, die nur auftreten, wenn der Lastverteiler eine Folgeanfrage an die andere Instanz schickt.
  • Verlorener Zwischenstand: Bei einem langen Ablauf mit mehreren Schritten ist nicht festgelegt, wo der Zwischenstand nach jedem Schritt gespeichert wird. Die bearbeitende Instanz behält den Zwischenstand deshalb nur im eigenen Arbeitsspeicher. Stürzt diese Instanz ab, ist der Zwischenstand verloren. Erkennbar ist das an Abläufen, die nach dem Neustart einer Instanz von vorn beginnen müssen.
  • Überlasteter Sitzungsspeicher: Bei jeder Anfrage liest die bearbeitende Instanz aus dem Sitzungsspeicher. Bei 1.000 Anfragen pro Sekunde muss der Sitzungsspeicher also 1.000 Lesezugriffe pro Sekunde beantworten, egal auf wie viele Instanzen der Lastverteiler die Anfragen verteilt. Zusätzliche Instanzen entlasten den Sitzungsspeicher deshalb nicht, und ab einer bestimmten Zahl von Anfragen kommt der Sitzungsspeicher nicht mehr nach. Erkennbar ist das an Antwortzeiten, die trotz zusätzlicher Instanzen nicht sinken, während die Last auf dem Sitzungsspeicher steigt.

Weiterführend

Verwandte Ansätze

  • Load Balancing: verteilt Anfragen auf mehrere Instanzen, so wie der Lastverteiler die Anfragen des Kunden an Instanz 1 und Instanz 2 weiterschickt.
  • Token Based Authentication: legt einen zeitlich begrenzten Anmeldenachweis in jede Anfrage, den jede Instanz selbst prüfen kann.
  • Twelve Factor App: eine Sammlung von Grundsätzen für Anwendungen. Einer der Grundsätze verlangt, dass laufende Programme nichts Dauerhaftes bei sich speichern und alles Bleibende in Datenbanken oder anderen gemeinsamen Speichern ablegen.

Quellen

  • Adam Wiggins: The Twelve-Factor App, VI. Processes: nennt laufende Programme Prozesse und verlangt, dass sie keinen eigenen dauerhaften Zustand haben. Für Sitzungsdaten empfiehlt die Seite einen gemeinsamen Speicher mit Ablaufzeit, und sie lehnt es ab, einen Kunden fest an ein bestimmtes laufendes Programm zu binden.
  • Microsoft: Design to scale out: Entwurfsgrundsatz aus dem Azure Architecture Center, nach dem jede Instanz jede Anfrage bearbeiten können soll und ein im Arbeitsspeicher gehaltener Sitzungsstand das Verteilen auf mehrere Instanzen behindert.