Assignment Chef icon Assignment Chef
All German tutorials

Programming lesson

Performance-Tuning eines skalierbaren Webdienstes: Engpässe erkennen und beheben

Lerne, wie du in einem simulierten Cloud-Webshop Engpässe identifizierst, Lasttests durchführst und durch horizontale Skalierung die Performance optimierst. Ideal für Studierende der verteilten Systeme.

Performance-Tuning skalierbarer Webdienst Engpassanalyse Bottleneck erkennen Cloud-Skalierung Java Webdienst Lasttest ServerLib verteilte Systeme Projekt 3 15.094 Caching Strategie Load Balancer horizontale Skalierung Ressourcenoptimierung Nichtdeterminismus Unzufriedene Kunden reduzieren

Einführung: Warum Performance-Tuning in der Cloud entscheidend ist

Stell dir vor, du betreibst einen Online-Shop – ähnlich wie die Plattform, die viele Studierende im Projekt 3 des Kurses 15.094 implementieren. Wenn plötzlich tausende Kunden gleichzeitig auf deine Website zugreifen, kann es zu Engpässen kommen. Genau hier setzt das Performance-Tuning an. Im Frühjahr 2025 hast du gelernt, wie man mit Java einen skalierbaren Webdienst entwickelt. Heute, am 6. Mai 2026, ist dieses Wissen aktueller denn je – denn auch moderne Apps wie TikTok oder ChatGPT müssen ähnliche Skalierungsprobleme lösen.

In diesem Tutorial erfährst du, wie du systematisch vorgehst, um die Performance deines Dienstes zu verbessern. Wir nutzen dabei eine simulierte Cloud-Umgebung, die dir erlaubt, verschiedene Strategien auszuprobieren, ohne echte Kosten zu verursachen. Das Ziel: möglichst viele Kunden zufrieden zu stellen und gleichzeitig die Ressourcen effizient einzusetzen.

Die Grundlagen: Was ist ein skalierbarer Webdienst?

Ein skalierbarer Webdienst kann wachsende Last bewältigen, indem er zusätzliche Server (VMs) startet. Unser Beispiel-Webshop besteht aus mehreren Tiers: einem Frontend, das Anfragen entgegennimmt, einem Middle-Tier, das die Logik verarbeitet, und einer Datenbank. Der Load Balancer verteilt die eingehenden Anfragen auf die Frontend-Server.

Typische Bottlenecks sind:

  • Die Datenbank, wenn zu viele Lese- oder Schreibzugriffe gleichzeitig erfolgen.
  • Der Middle-Tier, wenn die Verarbeitung komplexer Berechnungen dauert.
  • Der Frontend-Server, wenn die Anzahl der gleichzeitigen Verbindungen die Kapazität übersteigt.
Merke: Ein Engpass ist wie eine Flaschenhals – egal wie viel du vorne hineingießt, hinten kommt nur wenig raus. Deine Aufgabe ist es, den Flaschenhals zu finden und zu erweitern.

Schritt 1: Den ersten Engpass identifizieren

Bevor du optimierst, musst du messen. Starte deinen Dienst mit einer geringen Anzahl von simulierten Clients und erhöhe die Last schrittweise. Nutze die von der ServerLib bereitgestellten Methoden, um Metriken zu sammeln:

  • Anzahl der unzufriedenen Kunden (Clients, die abbrechen).
  • Durchschnittliche Antwortzeit pro Anfrage.
  • Auslastung der einzelnen Server (CPU, Speicher).

Ein gutes Werkzeug für solche Experimente ist ein selbst geschriebenes Testskript in Python oder Bash. Du kannst damit automatisiert verschiedene Lastprofile fahren und die Ergebnisse in einer Tabelle festhalten.

Beispiel: Lasttest mit 100 Clients

Angenommen, du startest 100 simulierte Kunden, die nacheinander Browse- und Purchase-Requests senden. Du stellst fest, dass 30% der Kunden abbrechen. Die Antwortzeit steigt auf über 10 Sekunden. Ein Blick auf die CPU-Auslastung zeigt: Der Middle-Tier-Server läuft bei 95% Auslastung, während der Frontend-Server nur bei 30% liegt. Das deutet darauf hin, dass der Middle-Tier der Engpass ist.

Schritt 2: Experimente zur Bestätigung des Bottlenecks

Um sicherzugehen, dass der Middle-Tier wirklich der Flaschenhals ist, führst du ein gezieltes Experiment durch: Starte einen zusätzlichen Middle-Tier-Server und wiederhole den Test. Wenn die Anzahl der unzufriedenen Kunden sinkt und die Antwortzeit fällt, hast du den Engpass bestätigt.

Wichtig: Nach dem Hinzufügen eines Servers kann sich der Engpass verschieben. Vielleicht ist jetzt die Datenbank der limitierende Faktor. Führe daher immer mehrere Experimente durch und dokumentiere die Ergebnisse.

Schritt 3: Techniken zur Behebung des Bottlenecks

Es gibt verschiedene Ansätze, um einen Engpass zu beseitigen:

  • Horizontale Skalierung: Füge weitere Server desselben Tiers hinzu. Das ist die einfachste Methode, aber sie kostet mehr Ressourcen.
  • Vertikale Skalierung: Erhöhe die Leistung eines einzelnen Servers (mehr CPU, RAM). In der Cloud oft teurer als horizontale Skalierung.
  • Optimierung des Codes: Manchmal reicht es, ineffiziente Algorithmen zu verbessern. Beispielsweise könntest du wiederholte Datenbankabfragen durch Caching vermeiden.
  • Lastverteilung anpassen: Vielleicht verteilt der Load Balancer die Anfragen nicht optimal. Ein Round-Robin kann durch einen Least-Connections-Algorithmus ersetzt werden.

Praxis-Tipp: Caching einführen

In unserem Webshop werden häufig dieselben Produktkategorien abgefragt. Statt jedes Mal die Datenbank zu befragen, könntest du die Ergebnisse im Arbeitsspeicher des Middle-Tiers zwischenspeichern. Das reduziert die Last auf die Datenbank erheblich. Implementiere dazu eine einfache HashMap in Java, die nach einer bestimmten Zeit verfällt.

Schritt 4: Skalierungssignale erkennen

Ein wichtiger Aspekt des Projekts ist es, zu erkennen, wann skaliert werden muss. Du solltest nicht warten, bis die Antwortzeiten explodieren. Stattdessen definierst du Schwellenwerte:

  • Wenn die CPU-Auslastung eines Servers über 80% steigt, starte einen weiteren Server.
  • Wenn die Anzahl der unzufriedenen Kunden 5% übersteigt, reagiere sofort.

Diese Signale können automatisiert werden. Du könntest einen Überwachungs-Thread in deinem Java-Code implementieren, der regelmäßig die Metriken prüft und bei Bedarf neue VMs startet.

Schritt 5: Mehrdimensionale Optimierung

In der Realität sind die Parameter voneinander abhängig. Beispielsweise beeinflusst die Anzahl der Frontend-Server die Last auf dem Middle-Tier. Eine Optimierung in einer Dimension kann eine Verschlechterung in einer anderen bewirken. Du musst also verschiedene Kombinationen testen.

Ein praktisches Vorgehen ist die Design of Experiments-Methode: Variiere systematisch die Anzahl der Server pro Tier und messe die Ergebnisse. Trage die Daten in eine Matrix ein und suche nach dem optimalen Punkt, an dem die Kundenzufriedenheit hoch und die Ressourcennutzung niedrig ist.

Beispiel aus der Praxis: Wie Netflix skaliert

Netflix verwendet eine ähnliche Strategie. Sie beobachten in Echtzeit, wie viele Nutzer streamen, und starten automatisch zusätzliche Server in der AWS-Cloud. Dabei achten sie darauf, dass die Kosten nicht explodieren. Auch du wirst in deinem Projekt einen Kompromiss zwischen Leistung und Kosten finden müssen.

Schritt 6: Mit Nichtdeterminismus umgehen

Verteilte Systeme sind nicht deterministisch – das bedeutet, dass zwei identische Experimente leicht unterschiedliche Ergebnisse liefern können. Ursachen sind Schwankungen in der Netzwerklatenz, der CPU-Last durch andere Prozesse oder der Zufall im Simulator.

Um zuverlässige Aussagen zu treffen, solltest du jedes Experiment mindestens dreimal wiederholen und den Durchschnitt berechnen. Notiere auch die Standardabweichung, um die Streuung zu erfassen.

Zusammenfassung und nächste Schritte

In diesem Tutorial hast du gelernt, wie du systematisch Engpässe in einem skalierbaren Webdienst identifizierst und behebst. Die Schritte sind:

  1. Bottleneck durch Messungen erkennen.
  2. Hypothese durch gezieltes Experiment bestätigen.
  3. Technik zur Behebung anwenden (Skalierung, Caching, Code-Optimierung).
  4. Neue Bottlenecks identifizieren und iterieren.
  5. Skalierungssignale definieren und automatisieren.
  6. Mehrdimensionale Optimierung durchführen.
  7. Nichtdeterminismus durch Wiederholungen ausgleichen.

Mit diesen Methoden bist du bestens gerüstet, um das Projekt 3 erfolgreich abzuschließen und auch in der Praxis skalierbare Dienste zu entwickeln. Viel Erfolg beim Tuning!