Application Performance Management

Projektrisiken frühzeitig erkennen und vermeiden

Wird die Performance-Qualität einer Software erst sehr spät überprüft, dann stellt sie stets ein hohes Projektrisiko dar. Die Spezifikation der erwarteten Performance zusammen mit einem frühzeitig gestarteten Application Performance Management (APM) vermindert die Kosten- und Terminrisiken.

Drei bis vier Wochen vor der Software-Abnahme stehen die Performance-Tests auf dem Plan. Dieser Zeitpunkt ist in der Regel zu spät angesetzt, um während der Tests aufgespürte Performance-Probleme noch rechtzeitig zu beheben. Die Verlängerung oder gar Öffnung der Testzeiträume würde allerdings die Kosten steigern, worauf sich kaum ein Stakeholder einlassen dürfte.

Ein möglicher Lösungsansatz kann sein, das bislang in der Phase des Software-Betriebs angesiedelte Application Performance Management in die Entwicklung nach vorne zu ziehen. Etwaige Performance-Probleme werden so bereits mit Beginn der Software-Entwicklung ersichtlich und Korrekturmaßnahmen lassen sich zeitnah initiieren. Ergebnis: Das Terminrisiko wird eliminiert, das Projektrisiko sinkt und die Software-Qualität steigt erheblich.

Ein erfolgreich vorgezogenes Application Performance Management muss mehrere Bedingungen erfüllen: Zunächst muss mit dem Kunden ein fachliches Mengengerüst mit definierten Antwortzeiten spezifiziert werden, das Teil des Anforderungspakets wird. Gemäß eines standardisierten Prozesses müssen dann auf Grundlage dieses Mengengerüsts Testszenarien definiert und fortlaufend Messungen durchgeführt werden. Und schlussendlich müssen die Messergebnisse ausgewertet und dem Prozess entsprechend Optimierungsmaßnahmen abgeleitet werden.

Was wir mit APM erreichen wollen

Die Ziele des Application Performance Managements überschneiden sich mit den Anforderungen an eine moderne Applikation. So soll etwa ein kurzes und schnelles Antwortzeitverhalten eine positive User-Experience (UX) schaffen. Je schneller die Software auf die Eingaben des Anwenders reagiert, desto produktiver und zufriedener wird der Anwender auch sein.

Einer Software lässt sich Performance nachträglich nur mit hohem Aufwand und innerhalb bestimmter Grenzen beibringen. Entweder die Software wurde schon von vornherein, also bereits während der Design-Phase, auf hohe Performance ausgerichtet und dies auch in der zugrundeliegenden Architektur berücksichtigt. Oder die fehlende Performance muss während des Betriebs der Software durch Kompromisse – unter Umständen sogar durch massiven Hardware-Einsatz – kompensiert werden. Im Nachgang lässt sich meist nur eine Notlösung finden.

Je früher im Entwicklungsprozess mit dem Application Performance Management begonnen wird, desto früher offenbaren sich die Differenzen zwischen Ist und Soll und desto eher lässt sich Abweichungen entgegenwirken. Das setzt kontinuierliche Performance-Messungen voraus, die über alle Phasen der Software-Entwicklung hinweg aufrecht erhalten werden und dann detailliert Aufschluss über die Performance-Qualität sowie ihren Verlauf geben.

Performance spezifizieren

Performance wird vom Kunden meistens implizit erwartet, aber selten genauer spezifiziert. Eine solche Grunderwartung ist jedoch nicht messbar oder vergleichbar. Deshalb muss die nebulöse Erwartung in konkrete Qualitätsmerkmale und Messkriterien überführt werden. Daraus leitet sich die Anforderung an Performance ab, die fortan als Soll-Marke dienen soll. Die Anforderung muss daher überprüfbar, mit dem Kunden abgestimmt und vor allem explizit in einer Spezifikation beschrieben sein. So kommen später keine Diskussionen um die Qualitätskriterien mehr auf.

Ein schriftlich fixiertes Mengengerüst von fachlichen Einheiten sieht zunächst eine Anzahl von zu bearbeitenden Anfragen pro Stunde oder eine Menge von Neukunden im Jahr vor. Es wird ergänzt um eine exakte Antwortzeit je Anwenderaktion. Daraus lässt sich ein Testszenario mit klar definierten Abnahmekriterien erstellen.

Bei fachlich zudem stark wachsenden Applikationen ist es entscheidend, über das fachliche Wachstum frühzeitig Bescheid zu wissen und dies auch zu spezifizieren. Andernfalls ruft das spätere Wachstum Probleme hervor, die sich voraussichtlich erst einige Monate nach dem Start der Produktivphase offenbaren.

Das Lastprofil – also die Anzahl gleichzeitiger Sessions oder die Anzahl der Anfragen innerhalb eines bestimmten Zeitraums, jeweils bezogen auf den Normalbetrieb und auch auf Spitzenlastzeiten – ist ebenso interessant. Wird die Applikation vermehrt am Morgen genutzt? Oder gibt es zum Ende eines jeden Quartals eine Lastspitze, weil mit der Applikation Abrechnungen erstellt werden? In welchem Land sitzen die Anwender? Kommt es zu Latenzen bei der Datenübermittlung, weil die Applikation von Südafrika aus genutzt wird und die Server in Europa stehen? In diesen Fällen muss Skalierbarkeit wegen starken Anwachsens des Datenvolumens und der Anfragemenge im Design einer Software berücksichtigt und mit einem geeigneten Testszenario abgesichert werden.

Neben einem Testszenario sind auch Testdaten in einem größeren Umfang notwendig. Die Datenbank der Integrationsumgebung sollte dabei den Datenumfang besitzen, der nach ein bis zwei Jahren Regelbetrieb vorliegen würde. Skripte erzeugen einen konsistenten und gleichen Testdatenbestand vollautomatisch und lassen sich in jeder beliebigen Skriptsprache erstellen.

Infografik 2

Roadmap: Erfolgreich wird Application Performance Management dann, wenn sowohl ein Mengengerüst als auch ein Lastprofil in der Anforderung spezifiziert sind. Anschließend muss die Performance konstant überwacht werden.

Iteratives Vorgehen

Sobald die Skripte zur Erzeugung der Testdaten vorliegen, können und sollten die Performance-Tests jederzeit und fortlaufend wiederholt werden. Die Ergebnisse offenbaren dann den Entwicklungstrend des Antwortzeitverhaltens über einen längeren Zeitraum.

Bei sehr leistungskritischen Geschäftsprozessen hat sich zudem ein gesondertes Testszenario als sinnvoll erwiesen, das täglich wiederholt wird. Welche Messungen dabei durchgeführt werden, hängt individuell von der Applikation ab. Für diese täglichen Tests eignen sich Lösungen wie JMeter, das sich einfach in ein Continuous-Integration-System wie Jenkins integrieren lässt. Die Testskripte werden vollautomatisiert im Nightly Build via Jenkins angestoßen, das wiederum nach jedem Durchlauf die Ergebnisse der angestoßenen Skripte grafisch aufbereitet. Die Trendentwicklung des Antwortzeitverhaltens wird damit ersichtlich.

Diese Vorgehensweise ermöglicht es, schnell und rechtzeitig in eine negative Performance-Entwicklung einzugreifen. Ein weiterer Vorteil besteht darin, das Performance-Verhalten der Applikation unter Last besser zu verstehen und eine genauere Abschätzung über deren zukünftige Entwicklung zu bekommen. Die nächsten Performance-Maßnahmen ergeben sich dann schrittweise mit jeder weiteren Iteration.

Zusammen mit dem Testszenario wird auch definiert, zu welchem Zeitpunkt eine Optimierung durchzuführen ist. Optimierungen erfolgen aber erst dann, wenn das Antwortzeitverhalten einer Testfallkette oder einer einzelnen Anfrage über einen Höchstwert rutscht, der zu stark vom Soll-Wert abweicht.

Performance Monitoring des Gesamtsystems

Es ist sinnvoll und empfehlenswert auf einer Integrationsumgebung neben einem Testfallszenario auch ein Performance Monitoring des laufenden Systems durchzuführen. Die Daten des Performance Monitorings ergänzen das durch das Testfallszenario gewonnene Bild der Applikation um den allgemeinen Zustand und einen Überblick über die Ressourcen des Systems, etwa Auslastung, Speicher und Threads. Diese Werte sind allesamt relevant für die Performance eines Systems.

Die Testfallszenarien sind natürlich nicht mit normalen Lasttestszenarien vergleichbar, da die Zahl der Anwender vernachlässigbar klein ist. Allerdings zeigen sich bestimmte Probleme auch schon unter geringer Last. Diese kleinen Probleme würden unter realer Last, etwa in einem späteren Produktivbetrieb, womöglich zu gewaltigen heranwachsen. Die Behebung dieser speziellen Art von Problemen muss deswegen besonders hoch priorisiert werden.

Überwachung auf zwei bis drei Ebenen

Die Erfahrung zeigt, dass es für ein Performance Monitoring ausreicht, die Applikation auf zwei bis drei Ebenen zu überwachen. Die erste Ebene, die überwacht werden sollte, ist die externe Serviceschnittstelle. Die nächste interessante Ebene für eine Messung stellt die Business-Schicht dar. Bei einer Java-EE-Applikation wäre dies idealerweise die Business-Fassade. Die dritte Ebene einer Messung sollte auf Höhe der Datenschicht erfolgen und alle Datenbankabfragen und Datenmanipulationen aufzeichnen, um jene zu identifizieren, die eine signifikante Last erzeugen.

Wichtig: Zu viele gleichzeitig konfigurierte Messpunkte sind kontraproduktiv, da der verwertbare Informationsgehalt sich in der Regel eher verschlechtert denn verbessert. So könnte es passieren, dass das Mess-Tool gar selbst zum Performance-Problem wird, weil es mit der Menge an Messpunkten nicht umgehen kann. Das darf auf keinen Fall passieren, weshalb es notwendig ist, eventuell auftretende Exceptions des Mess-Tools zu überwachen.

Bei einer hochverteilten Applikation muss nicht jeder einzelne Server eines Clusters überwacht oder instrumentiert werden. In der Regel ist es ausreichend, in jedem Netzwerksegment beziehungsweise jeder Netzwerkzone jeweils einen Server mit Messpunkten auszustatten, wenn die Fachlichkeit auf allen Infrastrukturressourcen repliziert ist. Es werden sonst so viele Daten gesammelt, dass die Auswertung unübersichtlich, unhandlich oder unmöglich wird. Abweichungen von dieser Vorgabe sind dann möglich, wenn sich etwa erst durch eine detailliertere Analyse ein Verständnis für ein Problem erarbeiten lässt. Grundsätzlich gilt: Je kleiner der Footprint des für die Messungen verwendeten Tools ist, desto besser.

Die getrennten Messebenen des Performance Monitorings setzen sich überdies zu einem Gesamtbild zusammen, das zusätzlich Korrelationen zwischen den Ebenen zeigt. Gibt es etwa einzelne, langsame oder extrem häufige Datenbankabfragen, dann ist dies sicherlich auch auf Ebene der externen Serviceschnittstelle erkennbar.

Zusätzlich Log-Dateien auswerten

Die erhobenen Messwerte lassen sich noch durch Auswertungen der Log-Dateien ergänzen. Dafür gut geeignet ist ein zentralisiertes Monitoring der Log-Ausgaben, etwa mit ELK-Stack. Es lässt sich so konfigurieren, dass die Protokolle elegant im Hintergrund geführt werden, ohne dabei das System mit einem weiteren Monitoring unnötig zu belasten. Seine Vorteile spielt das System insbesondere in hochverteilten Umgebungen aus.

Performance-Optimierungen im APM

Das Performance-Monitoring, also das Ergebnis der Lasttests, ist die Ausgangsbasis für die Optimierungsmaßnahmen. Die Reihenfolge der Optimierungsmaßnahmen wird dabei durch die Total-Cost-Metrik bestimmt. Die Metrik ermittelt das Produkt aus der Anzahl der Aufrufe sowie der Antwortzeit der Aufrufe und sortiert die Ergebnisse absteigend, wodurch die teuersten Aufrufe oben stehen.

Zunächst werden die ersten zwei bis fünf Aufrufe der Liste optimiert. Das sind die sogenannten Hotspots. Die Hotspots erzeugen erfahrungsgemäß bis zu 80 Prozent der Gesamtlast eines Systems. Sind die Hotspots optimiert, erfolgt ein weiterer Lasttest. Der zeigt meistens schon eine signifikante und unter Umständen höher als erwartet ausfallende Performance-Steigerung der Applikation. Denn allein die Optimierung einzelner Aufrufe wirkt sich meist leistungssteigernd auf die anderen Aufrufe aus.

Anstatt nach den Hotspots die Total-Cost-Liste weiter in absteigender Folge abzuarbeiten, werden die Aufrufkosten auf Basis des neuerlichen Lasttests neu berechnet. Denn es kann durchaus passieren, dass ursprünglich eher günstig bewertete Aufrufe nach der Hotspot-Optimierung plötzlich an die Spitze der Liste rutschen und neue Hotspots bilden. Etwa deshalb, weil sie nun deutlich häufiger aufgerufen werden können, als es bislang überhaupt möglich war. Aus diesem Grund erfolgt nach der Hotspot-Optimierung eine weitere Iteration aus Analyse, Auswertung und Hotspot-Optimierung.

Kreislauf: Nach jeder Hotspot-Optimierung, also der Behebung der größten Performancebremsen, beginnt der Kreislauf aus „Testen – Analysieren – Optimieren“ von vorne.

Best Practice zur Optimierung

Grundsätzlich hat sich folgende Best Practice bei der Optimierung als sinnvoll erwiesen: Niemals auf Vorrat optimieren! Das entspricht im Wesentlichen dem Paradigma „Do not design for future use!“. Probleme treten meist an Stellen auf, an denen vorher niemand welche vermutet hat, weshalb die Vermutung zu potentiellen Problemstellen nicht immer korrekt ist. Das ist letztendlich auch eine Frage der Effizienz im Entwicklungsprozess. Die vorhandenen Ressourcen sollen – allein schon aus wirtschaftlichen Gründen – gezielt für die Lösung eines Problems eingesetzt werden, und eben nicht, um auf Verdacht oder Vorrat zu optimieren.

Mögliche Optimierungsstrategien

Die Optimierungsstrategie orientiert sich an verschiedenen Faktoren: Es ist nicht immer möglich, eine Scale-Up- oder Scale-Out-Strategie zu verwenden, also leistungsstärkere oder mehre Server einzusetzen, da viele Kunden mit einer hohen Standardisierung in ihrem Rechenzentrum arbeiten. Eine Optimierung mit mehr oder schnelleren Ressourcen muss in der Regel sehr genau begründet werden, da die Optimierung mit höheren Betriebskosten einhergeht.

Bevor ein Scale-Up oder Scale-Out gegangen werden darf, müssen zunächst alle Optimierungsmöglichkeiten auf Ebene der Applikation geprüft werden. Ist etwa der Datenzugriff der Applikation optimal und performant? Es führt manchmal schon zu einem erheblichen Performance-Unterschied, wenn die Daten in vielen einzelnen Queries geladen oder aber die gleiche Menge an Daten mit der richtigen Join-Fetch-Strategie in einem einzelnen Query aggregiert geladen werden.

Müssen die Daten im Speicher nicht sekundenaktuell sein, dann kann eine Caching-Strategie eine probate Lösung sein. So zwingen verteilte Umgebungen oft dazu, die zeitliche Datenkonsistenz zu lockern und sich mit Eventual Consistency zufrieden zu geben. Dieser Ansatz der schlussendlichen Konsistenz wird heutzutage sehr gerne im Umfeld von Microservices gewählt, um den Durchsatz zu erhöhen. Verfügbarkeit und Skalierbarkeit haben in solchen Fällen eine höhere Bedeutung als eine sofortige Konsistenz.

Langlaufende Business-Transaktionen sollten generell vermieden werden, da wichtige Ressourcen wie Threads oder Daten zu lange blockiert werden. Es besteht die große Gefahr von sich gegenseitig blockierenden Zugriffen, bis zum Extremfall von Deadlocks. Falls diese Art der Verarbeitung dennoch notwendig sein sollte, etwa bei der Batch-Verarbeitung, so sind diese in die Nebenzeit zu verlagern, wo die Last auf das System gering ist. Falls es keine Nebenzeit gibt, etwa bei Online-Shops, dann kann eine Verlagerung der Batch-Verarbeitung auf einen separaten Server ebenfalls eine Lösung sein.

„Do not design for future use!”

Ressourcen schonen

Alle Optimierungsansätze haben zum Ziel, die Durchlaufzeit einer Anfrage zu reduzieren, damit Ressourcen nicht blockiert werden. Eine Halbierung des durchschnittlichen Antwortzeitverhaltens ermöglicht eine Verdopplung der gleichzeitig möglichen Anfragen an ein System – auf Basis von Little’s Law. Es werden insgesamt weniger System-Ressourcen benötigt, wenn die Applikation über alle Aufrufe performant ist und damit einen hohen Durchsatz von Anfragen und Daten (Throughput) ermöglicht.

Aus diesem Grund ist es interessant, verstärkt auf asynchrone Verarbeitung zu setzen. Der Extremfall davon ist der reaktive Programmierstil, bei dem alles asynchron über Nachrichten (Events) oder einem Futures and Promises Pattern verarbeitet und keine Ressource länger als notwendig blockiert wird.

Aus Architektursicht sollten Anwendungen möglichst zustandslos arbeiten. Nur mit diesem Ansatz ist es möglich, eine hochskalierbare Applikation zu erstellen. Mit dem Architekturstil REST kann dies beispielsweise realisiert werden.

Fazit

Application Performance Management sollte schon in der Design- und Entwicklungsphase einer Applikation angewandt werden, um stets die Performance-Anforderungen im Blick zu behalten, das Soll-Ziel einhalten und die Projektrisiken minimieren zu können. Performance-Tests sollten kontinuierlich durchgeführt werden. Ein ergänzendes Laufzeit-Monitoring verbessert die Analysemöglichkeiten.

Analyse und Optimierungen sollten immer iterativ durchgeführt werden, um spätere Überraschungen in einem Abnahmetest zu vermeiden. Codeoptimierungen oder Architekturanpassungen werden erst durchgeführt, wenn es tatsächlich Abweichungen von den Anforderungen gibt. Auf Vorrat oder Verdacht wird nicht optimiert.

Eine performante Applikation schont die Systemressourcen, senkt die Betriebskosten und verbessert die End-User-Experience.