Modulseite

Lektion 1 von 9

Was im Netz alles schiefgeht

Du verstehst, was sich ändert, sobald Programme über ein Netzwerk zusammenarbeiten, erkennst Teilausfälle und rechnest Latenz und Verfügbarkeit einer Aufrufkette aus.

ca. 40 Min.0/4 Checks gelöst

Worum es geht

Elena ist im 4. Lehrjahr als Informatikerin Applikationsentwicklung bei der Rheintal Logistik AG in Buchs SG. Die Sendungsverfolgung des Unternehmens ist ein einziges grosses Programm: Scans aus den Depots, Sendungsstatus und Kundenbenachrichtigung laufen im selben Prozess und schreiben in dieselbe Datenbank. In der Weihnachtszeit wird das Programm langsam, und weil alles zusammenhängt, steht dann auch die Disposition still. Elenas Teamleiter will einen Prototyp, der die Sendungsverfolgung in mehrere Dienste aufteilt.

Das klingt nach einer Umbauarbeit: Code in drei Projekte verschieben, Methodenaufrufe durch HTTP-Aufrufe ersetzen, fertig. Genau hier liegt der Denkfehler, an dem viele verteilte Systeme scheitern. Ein Aufruf über das Netzwerk sieht im Code fast gleich aus wie ein Methodenaufruf, verhält sich aber grundlegend anders: Er ist um Grössenordnungen langsamer, kann ganz ausfallen und kann, das ist das Tückische, mit unklarem Ergebnis enden.

In dieser Lektion lernst du, was sich ändert, sobald Programme über ein Netzwerk zusammenarbeiten. Du rechnest nach, was eine Aufteilung an Wartezeit und Verfügbarkeit kostet, und du bekommst das Grundvokabular, auf dem das ganze Modul aufbaut.

Vom Methodenaufruf zum Netzwerkaufruf

Ein verteiltes System besteht aus mehreren eigenständigen Programmen (Prozessen) auf verschiedenen Rechnern oder Containern, die nur über Nachrichten im Netzwerk zusammenarbeiten. Sie teilen keinen Arbeitsspeicher und keine gemeinsame Uhr.

Ein Bild aus dem Alltag: Wenn du in deinem eigenen Notizbuch nachschaust, hast du die Antwort sofort, und sie ist sicher da. Wenn du dagegen eine Kollegin in einer anderen Filiale anrufst, kann die Leitung besetzt sein, das Gespräch kann abbrechen, oder sie sagt «mach ich» und die Verbindung reisst ab, bevor du weisst, ob sie es wirklich gemacht hat.

Im Code sieht der Unterschied harmlos aus:

JavaScript
// Im Monolithen: ein Methodenaufruf im selben Prozess
const status = sendungsService.statusVon("RL-4711");

// Nach der Aufteilung: ein HTTP-Aufruf an einen anderen Dienst
const antwort = await fetch("http://sendungsstatus:8080/sendungen/RL-4711/status");
const statusAusDemNetz = await antwort.json();

Hinter der zweiten Variante steckt eine ganz andere Welt:

EigenschaftMethodenaufrufNetzwerkaufruf
DauerNanosekundenMillisekunden bis Sekunden
Mögliche AusgängeRückgabewert oder ExceptionErfolg, Fehler oder unklar (Timeout)
DatenReferenz auf ein ObjektKopie, muss serialisiert werden (JSON, Protobuf)
Gegenstelleläuft sicher, sonst läuft dein Programm auch nichtkann neu starten, überlastet oder weg sein
Reihenfolgegenau wie im CodeNachrichten können sich überholen

Ein paar Grössenordnungen helfen beim Einschätzen. Ein Methodenaufruf dauert wenige Nanosekunden. Ein Aufruf zwischen zwei Containern im selben Rechenzentrum braucht für Hin- und Rückweg rund eine halbe Millisekunde, dazu kommt die Verarbeitungszeit beim Empfänger. Das ist bereits zehntausend- bis hunderttausendmal langsamer. Geht der Aufruf von Buchs SG in ein Rechenzentrum im Ausland, sind es schnell 10 bis 20 Millisekunden. Solange du pro Anfrage einen einzigen Aufruf machst, merkt das niemand. Wenn eine Schleife 500 Sendungen einzeln über das Netz abfragt, schon.

Check 1 · Eine AntwortEinstieg

Elena ersetzt im Code sendungsService.statusVon(nr) durch einen HTTP-Aufruf an den Dienst Sendungsstatus. Was unterscheidet den neuen Aufruf grundlegend vom alten Methodenaufruf?

Acht Irrtümer über das Netzwerk

In den 1990er-Jahren haben Ingenieure bei Sun Microsystems (unter anderem L. Peter Deutsch und James Gosling) gesammelt, welche falschen Annahmen beim Bau verteilter Systeme immer wieder getroffen werden. Die Liste ist als Fallacies of Distributed Computing bekannt und heute noch genauso aktuell wie damals:

IrrtumWas in Wirklichkeit passiertBeispiel bei Rheintal Logistik
Das Netzwerk ist zuverlässig.Pakete gehen verloren, Verbindungen brechen ab.Der Handscanner verliert im Depot das WLAN mitten im Senden.
Die Latenz ist null.Jeder Aufruf kostet Zeit.Die Statusseite ruft drei Dienste nacheinander auf und wird spürbar träge.
Die Bandbreite ist unendlich.Grosse Datenmengen verstopfen die Leitung.Ein Dienst lädt bei jeder Anfrage die komplette Sendungshistorie.
Das Netzwerk ist sicher.Daten können mitgelesen oder verfälscht werden.Interne Aufrufe ohne TLS und ohne Authentifizierung.
Die Topologie ändert sich nicht.Container bekommen neue IP-Adressen, Dienste ziehen um.Fest einprogrammierte IP-Adresse des Status-Dienstes.
Es gibt einen Administrator.Verschiedene Teams und Firmen sind zuständig.Das SMS-Gateway gehört einem externen Anbieter.
Der Transport kostet nichts.Serialisieren und Übertragen kosten Rechenzeit und Geld.Jede JSON-Umwandlung braucht CPU, Cloud-Traffic kostet.
Das Netzwerk ist homogen.Verschiedene Systeme, Protokolle und Versionen.Alte Depot-Scanner senden ein anderes Format als neue.

Gewöhn dir an, bei jedem Netzwerkaufruf zu fragen: Welcher dieser Irrtümer steckt gerade in meiner Annahme?

Check 2 · ZuordnenEinstieg

Ordne jede Situation bei Rheintal Logistik dem Irrtum über Netzwerke zu, der dahintersteckt.

Teilausfälle: Erfolg, Fehler oder keine Ahnung

In einem einzelnen Programm gilt meistens: Entweder läuft alles, oder alles ist abgestürzt. In einem verteilten System ist der Normalfall der Teilausfall (partial failure): Ein Teil funktioniert, ein anderer nicht, und der funktionierende Teil weiss oft nicht genau, was beim anderen los ist.

Ein Aufruf über das Netzwerk hat deshalb nicht zwei, sondern drei mögliche Ausgänge:

  1. Erfolg: Die Antwort kommt an und meldet Erfolg, zum Beispiel HTTP 200.
  2. Klarer Fehler: Die Antwort kommt an und sagt, dass es nicht geklappt hat, zum Beispiel HTTP 400 oder 503.
  3. Unklar: Es kommt keine Antwort, der Aufruf läuft ins Timeout. Wurde die Anfrage verarbeitet oder nicht? Du weisst es nicht.

Der dritte Fall ist der Kern verteilter Systeme. Das Diagramm zeigt, warum: Der Benachrichtigungsdienst hat die SMS verschickt, aber die Antwort ging auf dem Rückweg verloren.

Für die Scan-Erfassung sieht eine verlorene Anfrage genau gleich aus wie eine verlorene Antwort. Wer nach einem Timeout wiederholt, riskiert eine doppelte Ausführung, wer nicht wiederholt, dass etwas gar nicht passiert. Die Werkzeuge dafür lernst du in diesem Modul: Nachrichten über einen Broker (Lektion 4), Timeouts und Retries (Lektion 5) und Idempotenz (Lektion 6).

Check 3 · Eine AntwortFortgeschritten

Die Scan-Erfassung schickt POST /sms an den Benachrichtigungsdienst. Nach 2 Sekunden bricht der Aufruf mit einem Timeout ab. Was weiss die Scan-Erfassung jetzt sicher?

Latenz und Verfügbarkeit nachrechnen

Bevor du ein System aufteilst, solltest du grob abschätzen, was es kostet. Dafür reichen zwei Regeln.

Regel 1: Latenzen. Die Latenz ist die Zeit vom Absenden einer Anfrage bis zum Eintreffen der Antwort. Rufst du mehrere Dienste nacheinander auf, addieren sich ihre Latenzen. Rufst du sie gleichzeitig (parallel) auf, bestimmt der langsamste die Wartezeit.

Regel 2: Verfügbarkeiten. Die Verfügbarkeit ist der Anteil der Zeit, in der ein Dienst funktioniert, zum Beispiel 99,9 %. Braucht eine Anfrage mehrere Dienste gleichzeitig, müssen alle laufen. Die Wahrscheinlichkeiten werden deshalb multipliziert: Bei drei Diensten mit je 99,9 % ergibt das 0,999 × 0,999 × 0,999 = 0,997, also 99,7 %.

Was das bedeutet, siehst du, wenn du in Ausfallminuten umrechnest. Ein Monat mit 30 Tagen hat 30 × 24 × 60 = 43'200 Minuten.

Dienste in der Kette (je 99,9 %)GesamtverfügbarkeitAusfall pro 30 Tage
199,90 %rund 43 Minuten
399,70 %rund 129 Minuten
599,50 %rund 216 Minuten
1099,00 %rund 430 Minuten (über 7 Stunden)

Schritt für Schritt: Was kostet die Aufteilung?

Elena will wissen, wie sich die Statusabfrage der Kundenwebsite nach der Aufteilung verhält. Im Monolithen dauert sie heute rund 40 ms. Im Prototyp gibt es drei Dienste: Sendungsstatus, Scan-Erfassung und Benachrichtigung. Für die Statusseite will das Marketing den letzten Scan und die letzte verschickte SMS anzeigen. Gemessene Werte aus dem Test (Netz = Hin- und Rückweg):

DienstNetzVerarbeitungVerfügbarkeit
Sendungsstatus1 ms12 ms99,9 %
Scan-Erfassung1 ms25 ms99,9 %
Benachrichtigung1 ms18 ms99,9 %

Schritt 1: Abhängigkeiten aufschreiben. Die Website ruft Sendungsstatus auf. Sendungsstatus fragt bei Scan-Erfassung (letzter Scan) und bei Benachrichtigung (letzte SMS) nach.

Schritt 2: Variante A, nacheinander. Alle Latenzen addieren: (1 + 12) + (1 + 25) + (1 + 18) = 13 + 26 + 19 = 58 ms.

Schritt 3: Variante B, parallel. Sendungsstatus fragt beide Dienste gleichzeitig: 13 + max(26, 19) = 13 + 26 = 39 ms.

Schritt 4: Verfügbarkeit. In beiden Varianten müssen alle drei Dienste laufen: 0,999³ = 0,997003, also 99,70 %. Parallel aufrufen macht schneller, aber nicht zuverlässiger.

Schritt 5: In Ausfallminuten umrechnen. (1 - 0,997003) × 43'200 = rund 129 Minuten pro Monat statt rund 43 Minuten mit nur einem Dienst.

Schritt 6: Variante C prüfen. Sendungsstatus speichert die letzten Scans und SMS selbst, weil die anderen Dienste ihm Ereignisse schicken (Lektion 4). Dann braucht die Abfrage nur noch einen Dienst: 13 ms und 99,9 %. Der Preis: Die Daten können ein paar Sekunden alt sein. Das ist für eine Sendungsverfolgung vertretbar.

Elena rechnet das nicht von Hand, sondern mit einem kleinen Skript, das sie für weitere Varianten wiederverwenden kann:

JavaScript
// kette.js: Latenz und Verfügbarkeit einer Aufrufkette abschätzen
// Start: node kette.js

const MINUTEN_PRO_MONAT = 30 * 24 * 60; // 43'200 Minuten in 30 Tagen

function latenzSeriell(aufrufe) {
  // nacheinander: Zeiten addieren sich
  return aufrufe.reduce((summe, a) => summe + a.netzMs + a.verarbeitungMs, 0);
}

function latenzParallel(aufrufe) {
  // gleichzeitig: der langsamste Aufruf bestimmt die Wartezeit
  return Math.max(...aufrufe.map((a) => a.netzMs + a.verarbeitungMs));
}

function verfuegbarkeit(dienste) {
  // alle müssen laufen: Wahrscheinlichkeiten multiplizieren
  return dienste.reduce((p, d) => p * d.verfuegbarkeit, 1);
}

const status = { netzMs: 1, verarbeitungMs: 12, verfuegbarkeit: 0.999 };
const scan = { netzMs: 1, verarbeitungMs: 25, verfuegbarkeit: 0.999 };
const info = { netzMs: 1, verarbeitungMs: 18, verfuegbarkeit: 0.999 };

const a = latenzSeriell([status, scan, info]);
const b = latenzSeriell([status]) + latenzParallel([scan, info]);
const p = verfuegbarkeit([status, scan, info]);

console.log(`Variante A (nacheinander): ${a} ms`);
console.log(`Variante B (parallel):     ${b} ms`);
console.log(`Verfügbarkeit A und B:     ${(p * 100).toFixed(2)} %`);
console.log(`Ausfall pro Monat:         ${Math.round((1 - p) * MINUTEN_PRO_MONAT)} Minuten`);
console.log(`Variante C (eigene Daten): ${latenzSeriell([status])} ms`);

Ausgabe:

Text
Variante A (nacheinander): 58 ms
Variante B (parallel):     39 ms
Verfügbarkeit A und B:     99.70 %
Ausfall pro Monat:         129 Minuten
Variante C (eigene Daten): 13 ms

Damit hat Elena ein klares Argument: Die Aufteilung lohnt sich nur, wenn die Statusabfrage nicht bei jeder Anfrage durch drei Dienste wandern muss.

Check 4 · Code-LaborEinstieg

Code-Labor: Latenz und Verfügbarkeit einer Aufrufkette. Elena will ihre Abschätzung aus der Lektion für beliebige Varianten wiederverwenden. Ergänze die vier Funktionen:

  • latenzSeriell(aufrufe): Summe aller netzMs + verarbeitungMs
  • latenzParallel(aufrufe): der langsamste Aufruf zählt
  • verfuegbarkeit(dienste): Produkt aller Verfügbarkeiten
  • ausfallMinutenProMonat(p): Ausfallminuten in 30 Tagen, auf ganze Minuten gerundet
Code-Labor · JavaScript
Strg + Enter

Typische Fehler

  1. Netzwerkaufrufe wie Methodenaufrufe behandeln. Eine Schleife, die für 500 Sendungen je einen HTTP-Aufruf macht, dauert plötzlich Sekunden. Korrektur: einen Sammel-Endpunkt anbieten (GET /sendungen?nr=RL-1,RL-2) oder die nötigen Daten lokal halten.
  2. Timeout als «fehlgeschlagen» behandeln. Wer nach einem Timeout annimmt, es sei nichts passiert, löst Aktionen doppelt aus. Korrektur: Timeouts als «unklar» behandeln und Empfänger so bauen, dass eine Wiederholung keinen Schaden anrichtet.
  3. Verfügbarkeiten mitteln statt multiplizieren. Drei Dienste mit 99,9 % ergeben nicht 99,9 %, sondern 99,7 %. Wer den Durchschnitt nimmt, unterschätzt die Ausfallzeit um das Dreifache.
  4. Aufteilen, ohne die Kosten zu rechnen. «Microservices sind modern» ist kein Argument. Jede Aufteilung bringt Latenz, Teilausfälle und Betriebsaufwand. Sie muss sich durch einen konkreten Nutzen lohnen.

Zusammenfassung

  • Ein verteiltes System besteht aus eigenständigen Prozessen, die nur über Nachrichten im Netzwerk zusammenarbeiten, ohne gemeinsamen Speicher und ohne gemeinsame Uhr.
  • Ein Netzwerkaufruf ist um Grössenordnungen langsamer als ein Methodenaufruf und kann ausfallen.
  • Jeder Aufruf hat drei Ausgänge: Erfolg, Fehler, unklar. Ein Timeout bedeutet «ich weiss es nicht».
  • Die acht Irrtümer (zuverlässiges Netz, Latenz null usw.) helfen dir, versteckte Annahmen im Code zu finden.
  • Latenz: nacheinander addieren, parallel zählt der langsamste Aufruf.
  • Verfügbarkeit: Bei zwingend nötigen Diensten multiplizieren. Jeder zusätzliche Dienst in der Kette senkt die Gesamtverfügbarkeit.
  • Weniger Abhängigkeiten pro Anfrage, zum Beispiel durch eigene Daten, machen ein verteiltes System schneller und robuster.

Mit deiner eigenen KI vertiefen

Kopiere einen Prompt in Claude, ChatGPT oder Claude Code. Er macht die KI zur Lernbegleitung statt zum Lösungsautomaten.

Sokratischer Coach für Teilausfälle Chat-KI

Du übst, bei einem Netzwerkaufruf alle Ausgänge durchzudenken, und die KI fragt nach, statt dir Antworten zu geben. Ideal nach Lektion 1.