14.03.2007, 09:00 Uhr
Detektiv auf Javas Fährte
Die Performance-Analyse von Java-EE-Anwendungen ist eine Detektivarbeit, bei der Tausende von Indizien zu berücksichtigen sind. Systematisches Vorgehen ist unerlässlich.
Martin Klapdor ist Senior Applications Engineer bei Opnet Technologies, Frankfurt am Main.
Eine klassische Situation: Ein Softwareprojekt steht kurz vor dem Abschluss. Im Lasttest wird geprüft, ob die neue Anwendung die geplante Nutzeranzahl mit der geforderten Antwortzeit bedienen kann. Treten jetzt Performance-Probleme auf, müssen sie schnell und präzise analysiert werden. Handelt es sich bei der neuen Anwendung um eine Java-EE-Applikation (Enterprise Edition), ist das allerdings eine knifflige Aufgabe. Denn die Performance-Analyse solcher Anwendungen kämpft mit einem spezifischen Problem: der Überfülle heterogener Zustandsinformationen.
Die Performance-Probleme äussern sich durch parallele Auslastungs- und Abweichungsphänomene auf mehreren Tiers und Applikationen, die in der Regel von verschiedenen Abteilungen mittels diverser Werkzeuge aufgezeichnet werden. Begleitet werden die Symptome von einem «Grund-rauschen» unzähliger weiterer Metriken, die in irgendeiner Form ein abnormales Verhalten aufweisen, mit dem Problem jedoch nichts zu tun haben. Eine erfolgreiche Performance-Analyse muss daher jene Informa-tionen herausfischen, die mit den mangelhaften Antwortzeiten kausal korrelieren.
Java EE: Kniffliger als üblich
Die systematische Analyse darf nicht von vornherein eine bestimmte Perspektive einnehmen, sondern muss von einem Panoramablick auf alle Tiers, Applikationen und gegebenenfalls auch Netzwerke ausgehen - und erst danach die Ursache schrittweise eingrenzen. Die technische Voraussetzung dafür ist eine integrierte Steuerung des Monitorings auf allen Tiers und die Möglichkeit der Korrelation der erhobenen Daten.
Die Performanceanalyse erfordert eine spezifischere Vorgehensweise als herkömmliche Lasttests. Man lässt jeweils nur eine bestimmte Transaktion laufen, um einzelne Parameter bewerten zu können. Man eliminiert Denkpausen, um die Gleichzeitigkeit der Transaktionen sicherzustellen. Und: Es werden nicht die Antwortzeiten, sondern Performance-Metriken innerhalb der verschiedenen Tiers und Applikationen überwacht.
Dabei ist die grundlegende Unterscheidung zwischen technischer Skalierung und steigender Antwortzeit wichtig. Wird die Last auf eine Anwendung kontinuierlich gesteigert, hört irgendwann die Durchsatzmenge auf, proportional zur Laststeigerung zu skalieren: Die Anwendung hat ihren Sättigungspunkt erreicht, die Durchsatzkurve geht in ein Plateau über. Steigt die Last weiter, fängt die Durchsatzkurve sogar an zu fallen.
Die Antwortzeitkurve verläuft meist ganz anders. Beispiel: Die Grenze der noch akzeptablen Antwortzeit wird erst bei knapp 160 An--wendern durchbrochen, obwohl die Anwendung technisch gesehen schon bei 70 Anwendern zu skalieren aufhörte. Ein dramatischer Anstieg der Antwortzeit ist erst zu verzeichnen, wenn die Durchsatzkurve sinkt, während gleichzeitig die Last weiter ansteigt.
Was ist «unnormal»?
Bei der Analyse kommt es darauf an, die technische Skalierungsgrenze zu analysieren. Um zu wissen, was in der Anwendung passiert, wenn sie zu skalieren aufhört, muss man wissen, welche Metriken beginnen, überproportional zu skalieren. Deshalb braucht es für jede Metrik einen Vergleichspunkt - einen Basiswert, der auf allen System- und Anwendungsschichten zu definieren ist. Das Vorgehen: Man fährt mit dem Lastgenerator eine Last, die unter der Bruchstelle der technischen Skalierung liegt. Bei dieser Last definiert man für alle Metriken: Der jetzige Wert sei Normalwert, die CPUAuslastung auf allen Servern sei normal, die Antwortzeit aller Java-Klassen und Servlets sei normal und so weiter. Dann lässt sich feststellen, welche Komponenten bei steigender Last überpropor-tional skalieren. Damit grenzt sich die Anzahl der für die Problemlösung überhaupt relevanten Indizien ein.
Bei welcher Last der Basiswert kalibriert wird, hängt von der jeweiligen Fragestellung ab. Eine Last von 50 bis 75 Prozent der Skalierungsgrenze eignet sich, um zu analysieren, was sich verändert, wenn die Skalierung aufhört. Aber auch ein Wert jenseits der Grenze kann wertvolle Ergebnisse liefern. Denn dann werden Metriken der Komponenten, die in der problema-tischen Transaktion vor dem Flaschenhals liegen, relativ konstant bleiben, die Metriken hinter dem Flaschenhals jedoch deutliche Abweichungen aufweisen. Auf diese Weise kann bereits eine einfache Analyse sehr schnell auf die Problemursache deuten. Die Voraussetzung für ein solches Verfahren ist eine integrierte Agentensteuerung. Agenten auf allen Tiers müssen synchron in einen speziellen Modus versetzt werden, in dem sie eine «normale» Bandbreite von Werten kalibrieren.
Rangliste der Abweichler
Nachdem für alle Metriken ein Normalwert definiert wurde, ist für die verschiedenen Laststufen eine Analyse der am stärksten vom Normalwert abweichenden Metriken möglich (siehe Bild S. 18.). Denn diese sind es, die direkt mit dem Flaschenhals zu tun haben oder davon betroffen sind. Dabei eröffnen sich häufig Zusammenhänge, an die man zuvor nie gedacht hätte. Bei diesem Schritt muss das Analysewerkzeug allerdings differenziert arbeiten: Es muss sowohl Ausmass als auch Dauer der Abweichungen berücksichtigen. Sonst verfälschen einzelne Ausreisser das Bild.
Als nächstes wird für eine Laststufe eine Rangliste der schwerwiegendsten Abweichungen erstellt. Hat man schon eine Idee, auf welcher Systemschicht das Problem angesiedelt sein könnte, kann man die Analyse darauf fokussieren. Sonst fährt man sie über alle Komponenten hinweg. Die Aussagekraft der Rangliste ist aber auch dann beträchtlich, denn in der Regel erhält man eine relativ kleine Zahl von Metriken, die signifikant stärker als alle anderen abweichen. Jetzt kann der Analyst mit grosser Wahrscheinlichkeit darauf schliessen, dass eine bestimmte Anzahl von Komponenten mit dem Performance-Problem zusammenhängen. Wie, weiss er allerdings noch nicht genau.
Zeitliche und kausale Korrelation
Die Abweichungsrangliste sagt zunächst nur, dass innerhalb einer Laststufe eine Korrelation zwischen unterschiedlichen Komponenten besteht. Es könnte sich um eine zufällige Koinzidenz handeln - weshalb eine Korrelationsanalyse über mehrere Laststufen hinweg erfolgen muss. Im Unterschied zur Abweichungsrangliste untersucht diese, ob zwei Metriken innerhalb eines eng definierten Zeitfensters, zum Beispiel fünf Sekunden, analog skalieren. Einmal angewandt, liefert die Analyse also nur einen zeitlichen Zusammenhang. Deshalb speichert das Werkzeug alle Korrelationspaare in einer Datenbank. In der nächsten Laststufe folgt eine erneute Korrelationsanalyse, deren Ergebnisse wiederum gespeichert werden und so weiter. Das Ergebnis ist eine Gewichtung nach der «Stärke» der Korrelation. Beispiel: In 95 Prozent aller Fälle korreliert A mit B. Hier liegt ein kausaler Zusammenhang nahe. A korreliert aber nur in 20 Prozent aller Fälle mit C. Hier besteht ein unklarer Kausalzusammenhang. Übrig bleiben vielleicht ein oder zwei Dutzend Komponenten, bei denen klar ist, dass sie kausal mit dem Performance-Problem verknüpft sind.
Java-Transaktionen aufgeschlüsselt
Bei komplexen Problemkonstellationen kann es jedoch mitunter notwendig sein, den exakten Ablauf der Methodenaufrufe innerhalb der Java Virtual Machine transparent zu machen. Damit lässt sich der Ursache-Wirkungs-Mechanismus innerhalb einer Transaktion präzise rekonstruieren. Sinnvoll ist dies aber erst, wenn die relevanten Parameter bereits bekannt sind. Denn dazu gilt es, einen Schnappschuss des aktiven Java-Codes in dem Augenblick zu erstellen, in welchem die problematische Transaktion abläuft. Zu diesem Zweck werden Trigger auf bestimmte Systemereignisse angesetzt, etwa die Aktivität einer Java-Klasse oder das Überschreiten von Performance-Grenzwerten. Im Anschluss stellt das Analysetool dar, in welcher Reihenfolge welche Komponenten einander aufgerufen und wie lange die einzelnen Transaktionsschritte gedauert haben. Man erkennt so in etwa, welche Methoden besonders lange Laufzeiten hatten, von wo diese aufgerufen wurden und welche anderen Komponenten wegen der Verzögerung warten mussten. Wurden einzelne Komponenten als Ursache identifiziert, kann der Analyst mittels Drill-Down auf einzelne Programmsequenzen verzweigen, um die Transaktion genauer zu untersuchen.
Die Performance-Analyse von Java-EE-Anwendungen muss eben nicht nur die Kluft zwischen den einzelnen Tiers und Applikationen überwinden, sondern auch die zwischen System und Netzwerk.
In der Praxis
Der Workflow der Performance-Analyse
o Lasttest zur Erstellung einer Performance-Prognose
Werden geforderte Antwortzeiten erreicht?
Wenn nein:
o einzelne Transaktion auswählen
o kurzer, stufenweise gesteigerter Lasttest zur Bestimmung der technischen Skalierungsgrenze
o Kalibrieren eines Basisschwellenwerts für alle Agenten einer Laststufe
o Stufenlasttest bis über die Grenze der technischen Skalierbarkeit hinaus
o Analyse der Ergebnisse
o Änderungen beziehungsweise Korrekturen
o neuer Stufenlasttest für diese Transaktion
o neuer gemischt-realistischer Lasttest
Martin Klapdor