Projekt Last- und Performancetests / SLA-Monitoring

Aus THM-Wiki
Wechseln zu: Navigation, Suche
Kurs
Kursname Web Performance
Kurstyp Projektkurs
Dozent Dr. Klaus Quibeldey-Cirkel
Semester SoSe 2007
Studiengang Master


Die Projekte Last- und Performancetests / SLA-Monitoring und Performanter Webserver wurden im Rahmen der Diplom-/Master-Veranstaltung Web-Applications im Sommersemester 2007 durchgeführt. Dieses Dokument beschreibt die Aufgaben und Ergebnisse des Teams "Browser".

Analyse der Portalsoftware

In der eingehenden Analyse der Portalsoftware wurden in eStudy die

  • Anwendungsbereiche
  • Geschäftsvorfälle
  • Benutzeraktionen
  • Szenarien
  • Testfallketten

identifiziert und in folgendem Excel-Dokument dokumentiert: Media:TestCases_2007-05-30-1.zip

Spezifizieren der IT-Infrastruktur

IT-Infrastruktur.PNG

Ansprechpartner bei Ausfall der IT-Infrastruktur

Fachhochschule Giessen-Friedberg IT-Services
Tel: 0641/309-1280
E-mail: its@fh-giessen.de
Web: http://www.dvz.fh-giessen.de/
Adr: Moltkestraße 3 (Haus H, Raum 105)

Überwachung

DVZ-Server und -Netzwerk-Komponenten werden 24/7 überwacht durch HP OpenView und Nagios.

Mengengerüst und Lastprofil

Um ein möglichst realistisches und praxisnahes Mengengerüst in den Test-Skripten aufbauen zu können, wurde eine Analyse der Aufruf-Statistik von eStudy durchgeführt.

Mengengerüst v2.png

Das obige Diagramm stellt den Verlauf der Aufrufe pro Monat für jeden Bereich in eStudy dar. Dabei wird besonders die semesterabhängige Lastverteilung deutlich.

Das folgende Diagramm gruppiert die Zugriffe nach Bereichen und zeigt, dass sich die Bereiche Forum, News, Ressourcen, User, Courses und Photogallery deutlich gegenüber den restlichen Bereichen abheben.

Mengengerüst v2 2.PNG

Aufbauend auf den Detail-Auswertungen, sowie verbleibender Zeit und Manpower wurde beschlossen, den Fokus für die Erstellung der Test-Skripte auf die jeweils meistaufgerufenen Bereiche und Skripte zu legen:

(in Klammern jeweils die relative Aufruf-Häufigkeit)

  • Forum
/showtopic.php (19)
/ (10)
/board.php (10)
/reply.php (2)
/newtopic.php (1)
  • Resourcen
/filemanager.php (68)
/insertfile.php (3)
/insert.php (1)
  • User
/homepage.php (4)
/memberlist.php (3)
/overview.php (1)
  • Courses
/treeoverview.php (8)
/courseinfo.php (2)
/register.php (1)
  • Photogallery
/newtopic.php (3)
/showtopic.php (1)
/board.php (1)
/index.php (1)

Lastprognose

Anhand der Richtlinien des Kundens für den FH-weiten Einsatz von 5000 - 10000 Studenten und 250 Dozenten, sowie dem Stand Mai 2007 der eStudy-Statistik wurden für die Prognose zwei Faktoren ermittelt:

  • Faktor ~7,5 für Studenten
  • Faktor 5 für Dozenten


Die Faktoren für die übrigen Bereiche wurden davon abgeleitet:

  • Für Bereiche, die hauptsächlich von der Dozentenzahl abhängig sind (wie Kurse, Kursforen, Umfragen, Mitteilungen) wurde Faktor 5 verwendet.
  • Für Bereiche, die hauptsächlich von der Studentenzahl abhängig sind (wie Unterforen und Beiträge) wurde der "studentische Faktor" 7,5 verwendet.


Stand Mai '07 Faktor Prognose
Portalmitglieder Gesamt 1066 7,5 7995
Studenten 1000 7,5 7500
Dozenten 42 5 210
Sekretariat 22 5 110
Tutoren 36 5 180
Kurse Anzahl 119 5 595
davon aktiv 60 5 300
Teilnehmer 713 5 3565
Foren Anzahl Global 61 5 305
Unterforen 389 7,5 2918
Themen 1407 7,5 10553
Beiträge 5039 7,5 37793
Beteiligung (User) 200 7,5 1500
Umfragen Anzahl 40 5 200
Stimmen 575 7,5 4313
Mitteilungen Anzahl 568 5 2840
Autoren 31 5 155
Ressourcen Anzahl 5482 7,5 41115
Verzeichnisse 1398 7,5 10485
Dateien 3789 7,5 28418
Links 295 7,5 2213
Gesamtgröße Dateien (MB) 3779 7,5 28343
Datei-Downloads 93706 7,5 702795
Link-Klicks 4432 7,5 33240

Testskriptkonventionen

Es sind mehrere Personen des Testteams in die Erstellung der Skripte für die Lasttests involviert. Die Testfälle werden dabei unabhängig voneinander als Testskripte für JMeter umgesetzt. JMeter bietet eine relativ große Freiheit bei der Strukturierung seiner Skripte. Für die meisten Aufgaben gibt es mehrere Lösungsmöglichkeiten. Um die von einzelnen Teammitgliedern erstellten Skripte für das ganze Team nachvollziehbar zu machen, werden an dieser Stelle Konventionen eingeführt, die bei der Testerstellung berücksichtigt werden müssen.

Begriffe

Die wichtigsten Begriffe, die bei der Testerstellung verwendet werden sind:

AUT
Anwendung unter Test (Application Under Test), die zu testende Applikation
Test
Ein Test ist eine Folge von simulierten Benutzeraktionen (funktionaler Test) oder ein Aufruf einer Funktion (Modultest) mit genau definierten konkreten Eingabedaten/Argumenten und den Erwartungswerten/dem Rückgabewert.
Testdatum
Ein Testdatum ist ein Wert, mit dem der Test ein Eingabefeld füllt (funktionaler Test) oder der als Argument der zu testender Funktion (Modultest) dient. Testdaten sind simulierte Benutzereingaben. Im Fall eines Lasttests auf der HTTP-Ebene entsprechen Testdaten den Requestparameterwerten.
Testdatensatz
Ein Testdatensatz ist die Menge aller Testdaten für einen Test.
Testfall
Ein Testfall ist die Verallgemeinerung eines Tests. Testfälle simulieren Anwendungsfälle, ohne dabei konkrete Eingabedaten zu definieren. Für einen Testfall kann eine Menge von Ausprägungen erstellt werden, indem er durch jeweils unterschiedliche Testdaten ergänzt wird.
Testszenario
Eine definierte Sammlung von Tests, die zu unterschiedlichen Testfällen gehören können, mitsamt der relativen Häufigkeit der Testsfälle. (Bsp: Testszenario1 = Testfall A(5%), Testfall B(80%), Testfall C (15%))
Zusicherung
Eine Zusicherung (Assertion) beschreibt einen erwarteten Zustand der AUT an der definierten Stelle des Testfalls. (vgl. JUnit-Asserts.) Bei der Ausführung von Lasttests mit JMeter können Zusicherungen benutzt werden, um einen fehlgeschlagenen Seitenaufruf zu protokollieren.
Testskript
Ein Testskipt ist eine maschinenlesbare Darstellung eines Tests oder eines Testfalls. (Im letzteren Fall werden konkrete Testdatensätze auf externe Quellen ausgelagert, auch JMeter verfährt so.) Das zugehörige Testtool interpretiert Testskripte und wandelt sie in simulierte Benutzeraktionen um.
Testergebnis
In den funktionalen bzw. Modultests besteht das Testergebnis aus den Ergebnissen der einzelnen Zusicherungen. Die Ergebnisse protokollieren die Übereinstimmung der tatsächlichen Rückgabewerte mit den Erwartungswerten. Bei den Lasttests kommen zusätzlich die Antwortzeiten als Testergebnisse hinzu.

Verzeichnisstruktur

Es existiert ein Ordner für jedes Testskript, der den Namen des Testfalls in Englisch trägt. Der Ordnername besteht aus genau einem Wort, die einzelnen Wörter des Testnamens werden zusammen geschrieben und fangen jeweils mit einem Großbuchstaben. (z.B. RegisterUser, BrowseForum, DeleteCourse).

In jedem Ordner liegt ein Testskript, das genauso heißt, wie der Ordnername (plus .jmx-Erweiterung, z.B. RegisterUser.jmx). Zusätzlich liegen in dem Ordner die Testdatendateien in CVS-Format. Jede Testdatendatei bezieht sich auf eine Tätigkeit im Testskript. Ihr Name hat folgende Struktur: testdata.<Testfallname>.<Aktivitätname>.csv, z.B. testdata.BrowseForum.Login.csv, testdata.BrowseForum.CreateThread.csv.

Außerdem enthält der Ordner eine Datei namens Readme.txt, die den Testfall kurz beschreibt und vor Allem die Struktur des Testskriptes grob erläutert. Die Erläuterung umfasst alle Aktivitäten im Testskript und die Beziehungen zu den Testdatendateien (vor Allem wenn mehrere existieren). Falls bekannt, sollen hier auch die Datenbanktabellen angegeben werden, die von dem Test geändert werden.

Skriptaufbau

Jedes Skript besteht aus einer Menge von Threadgruppen. Jede Threadgruppe beschreibt einen Testfall. Alle Threadgruppen eines Testskriptes werden während der Ausführung parallel verarbeitet. Meistens besteht ein Skript aus genau einer Threadgruppe.

An eine Threadgruppe sind drei Arten von Testelementen angehängt und zwar in dieser Reihenfolge: Konfigurationselemente, Aktivitäten, Listener (Beobachter).

Die Konfigurationselemente kapseln die Einstellungen des Testskriptes, die für viele Testelemente relevant sind und sich oft ändern. Die Konfigurationselemente sind in folgender Reihenfolge angegeben:

  • Konfigurationselemente für CSV-Datensätze
Hier werden die Referenzen auf die Testdatendateien gehalten (Achtung: hier dürfen nur die globalen Daten stehen. Dabei wird der Datensatzzeiger, der sich auf die Testdatendatei bezieht, für jeden Thread um eine Stelle verschoben. Sollen mehrere Datensätze in einem Thread durchlaufen werden, wie es z.B. bei den Wiederholungskontrollern geschieht, dann müssen die entsprechenden Datensatz-Kontroller innerhalb des Wiederholungskontrollers liegen.)
  • Benutzerdefinierte Variablen
Hier kommen die Variablen rein, die der Tester selbst definiert hat, wie z. B. der Pfad der AUT
  • Default-HTTP-Request Daten
Platz für http-Requestdaten, die für die meisten Requests gleich sind, also URL, Port und Protokoll.
  • Labels
Statt die deutschen Textlabels direkt zu referenzieren, werden sie in diesem Konfigurationselement parametrisiert. Die Werte müssen dann nur hier geändert werden, wenn die Sprache der AUT z.B. auf Englisch umgestellt wird.
  • Cookie Manager
Dieses Konfigurationselement muss immer dabei sein, damit Cookies gespeichert werden können. (Wichtig für Session-Management.)

Die Aktivitäten sind logische Kontroller, die zusammenhängende Requests kapseln. Die Aktivitäten können verschachtelt sein. Eine Aktivität kann im Wesentlichen nur ein einfacher Kontroller für bessere Übersicht sein (vgl. Java-Packages) oder aber ein Schleifen-Kontroller, der die beinhalteten Requests mehrfach ausführt. Weitere Kontroller sind erlaubt, müssen dann aber durch sprechende Bezeichner gekennzeichnet werden.

Innerhalb der Kontroller befinden sich die Request-Beschreibungen in der Reihenfolge ihres Auftritts. Zwischen den Request-Beschreibungen können Pre/Postprozessorenelemente vorkommen. Um eine zur Testerstellungszeit unbekannte Variable aus einem Response zu extrahieren, soll der Postprozessor Regular Expression Extractor verwendet werden. Die Variable, die er mit dem neuen Wert belegt, muss dann vorher im Konfigurationselement Benutzerdefinierte Variablen deklariert werden.

In jedem Request sind die Optionen „Frage alle Bilder und Applets an“ und „Folge Redirects“ aktiviert. An jedes Request-Element ist eine Zusicherung als Kindknoten angebunden. Sie stellt sicher, dass das Response der angeforderten Webseite entspricht. Außerdem enthält jedes Requestelement einen Antwortzeitbeobachter, mit dem die Testzeiten für dieses Request gemessen werden.

Requests, die mit einer Nachdenk- bzw. Eingabezeit seitens des simulierten Benutzers verbunden sind, enthalten einen Verzögerungstimer. Diesem werden die maximale und die minimale Wartezeit übergeben. Der Timer berechnet zur Laufzeit einen Zufallswert aus dem angegebenen Zeitinterval und verzögert das Request entsprechend.

Unter der Liste der Hauptaktivitäten befinden sich die Hauptbeobachter. Das sind mindestens der Zusicherungsbeobachter, der tabellarische Testzeitbeobachter, der Response-Beobachter und die grafische Darstellung des Zeitverhaltens.

Bezeichnerkonventionen

Generell haben alle Testskriptelemente englische Bezeichner. Die Bezeichner der Requests sind imperativ (als Anweisungen) angegeben und können aus mehreren getrennten Wörtern bestehen, die alle mit einem Großbuchstaben anfangen (Register User, Create New Thread usw.). Einfache Seitenübergänge haben die Form Show <Dialogname> Dialog. Die Loginprozedur besteht z.B. aus zwei Requests: Show Login Dialog (Get-Request) und Login (Post-Request). Die Aktivitätenbezeichner haben sprechende Namen und enden mit Activity im Fall eines einfachen Kontrollers, bzw. Activities im Fall eines Wiederholungskontrollers. (Beispiele: Login Activity, Create Thread Activities). Zusicherungsnamen haben die Form Assert <Dialogname> Dialog, die Timernamen entsprechen der Form Wait For <Condition>. (Beispiele: Assert Welcome Dialog, Assert Thread Created Dialog, Wait For Login Data Input, Wait For Submitting). Die Beobachterbezeichner enden mit dem Wort Result bei requestgebundenen, bzw. mit Results bei globalen Beobachtern. (Beispiele: Assertion Result, General Assertion Results, Test Time Results). Die Konfigurationselemente vom Typ Regular Expression Extractor fangen mit dem Wort Extract an (Beispiele: Extract Node Id, Extract Student Name).

Die Konfigurationselemente haben auch standardisierte Namen. Die CSV-Datensatz- Konfigurationselemente fangen mit dem Wort Testdata an und deuten in ihrem Namen auf die Aktivität, welche die Testdaten benutzt, z.B. Testdata Login, Testdata Create Thread, Testdata Reply usw. Das Element mit den benutzerdefinierten Variablen trägt den Namen Variables. Das Konfigurationselement für die Einstallung der HTTP-Requestdaten heißt entsprechend Default HTTP Request Data. Ebenso verhält es sich mit dem Cookie Manager Element, das Cookie Manager heißt. Das Element, welches die Labels kapselt, heißt German Labels für der deutsche Version von eStudy.

Die Bezeichner der Variablen innerhalb von Konfigurationselementen entsprechen folgenden Konventionen:

  • Alle Variablennamen werden kleingeschrieben, Teilwörter sind mit Unterstrichen verbunden.
  • Die Testdatenvariablen, die den Spalten der CSV-Dateien entsprechen, haben die Form dat_<activity>_<column> (dat_login_username, dat_login_password, dat_createthread_posttopic, dat_createthread_posttext)
  • Benutzerdefinierte Variablen können beliebig benannt werden, solange sie dem 1. Punkt entsprechen und englisch sind.
  • Unter den benutzerdefinierten Variablen muss die Variable path vorkommen, die den relativen Pfad der AUT auf dem Server als Wert hat (z.B. /~wa-browser, falls die Adresse der AUT www.estudy-portal.de/~wa-browser lautet).
  • Die Labelkonstanten haben die Form lbl_<sprechender Name>, z. B. lbl_login für Einloggen, lbl_submit für Abschicken, lbl_submit für Submit Form.

Automatisierte Testausführung

Durch das automatisierte Ausführen der Tests sollen folgende Anforderungen erreicht werden:

  • Möglichst schnelle Testausführung ohne Einwirkung von Menschen
  • Erstellung von maschinenlesbaren, universal interpretierbaren Testergebnissen
  • Unabhängigkeit der Tests voneinander (Reihenfolge der Tests soll egal sein)
  • Reproduzierbarkeit der Tests
  • Parallele Ausführung von verschiedenen Tests

Automatisierung der Testausführung mit Ant

JMeter kann automatisiert ausgeführt werden, indem es in das Build-Werkzeug Ant integriert wird. Dazu kann die Ant-Taskbibliothek ant-jmeter.jar benutzt werden, die unter [1] heruntergeladen werden kann.

Diese Bibliothek enthält die Task jmeter, welche im einfachsten Fall drei Parameter in Form von XML-Properties erwartet. Das sind die Angaben für JMeter Home-Verzeichnis, und die Pfade von dem auszuführenden Testskript und der Testergebnisdatei.

<jmeter
   jmeterhome="c:\jakarta-jmeter-1.8.1"
   testplan="${basedir}/loadtests/JMeterLoadTest.jmx"
   resultlog="${basedir}/loadtests/JMeterResults.jtl"/>

Statt eines Testskripts kann ein Test-Verzeichnis angegeben werden, in dem sich mehrere Testskripte befinden. Diese werden dann nacheinander ausgeführt.

<jmeter
   jmeterhome="c:\jakarta-jmeter-1.8.1"
   resultlog="${basedir}/loadtests/JMeterResults.jtl">
      <testplans dir="${basedir}/loadtests" includes="*.jmx"/>
</jmeter>

Einzelne Properties, die in den JMeter-Testskripten definiert wurden, können von Ant aus geändert werden. So überschreibt das nachfolgende Beispiel die Anzahl der parallelen Tests und die Anzahl der Testdurchläufe.

<jmeter
   jmeterhome="c:\jakarta-jmeter-1.8.1"
   testplan="${basedir}/loadtests/JMeterLoadTest.jmx"
   resultlog="${basedir}/loadtests/JMeterResults.jtl">
      <property name="request.threads" value="1"/>
      <property name="request.loop" value="10"/>
</jmeter>

Formatierung der Testergebnisse

Die Testergebnisse von JMeter liegen in der angegebenen .jtl-Datei. Das Format der Datei kann csv oder xml sein. Standardmäßig ist das csv-Format eingestellt. Um eine View der Ergebnisse darzustellen, sollte das Format auf xml umgestellt werden. Dazu ändere man die Zeile

jmeter.save.saveservice.output_format=csv

in der properties-Datei von JMeter (bin-Verzeichnis) auf:

jmeter.save.saveservice.output_format=xml

Um eine Übersicht der Fehler darzustellen setze man noch die Eigenschaft

jmeter.save.saveservice.assertion_results=all

Mit diesen Einstellungen haben die Testergebnisse die Form einer Liste von XML-Elementen, von denen jedes ein einzelnes Request-Testergebnis beschreibt. Das folgende Beispiel zeigt ein Request-Testergebnis aus dem Registrierungstestfall des eStudy-Portals.

<httpSample
   t="187"
   lt="187"
   ts="1179222813203"
   s="true"
   lb="ShowMainDlgRequest"
   rc="302"
   rm="Found"
   tn="User 1-1"
   dt="text">
      <assertionResult>
         <failure>false</failure>
         <error>false</error>
      </assertionResult>
</httpSample>

Die Attribute des Elements sind die üblichen Testergebnisse wie Response-Wartezeiten, Benutzer/Threadname, Requestname usw. Diese Werte werden normalerweise kommagetrennt in der Ergebnisdatei dargestellt, falls das Ergebnisformat auf csv eingestellt wird.

Das Unterelement assertionResult zeigt das Ergebnis einer im Testskript definierten Zusicherung. In diesem Beispiel wurde diese Zusicherung erfüllt, deshalb haben die Inhalte von failure- und error-Elementen die Werte false. Im Falle der Verletzung dieser Zusicherung stünden hier die Abweichungen von den Erwartungen.

Da die Testergebnisse xml-Form aufweisen, können sie mit XSLT in HTML-Views transformiert werden. Zwei Beispiele für die Konstruktion von Views (jmeter-results-report.xsl und jmeterresults-detail-report.xsl) findet man ebenfalls unter [2]

Die Erstellung von Views wird ebenfalls von Ant übernommen. Dafür kann die xslt-Task verwendet werden, wie das folgende Beispiel zeigt:

<xslt
   in="${basedir}/loadtests/JMeterResults.jtl"
   out="${basedir}/loadtests/JMeterResults.html"
   style="${basedir}/loadtests/jmeter-results-report.xsl"/>

Bereinigung des Modellzustands

Eine wichtige Anforderung an die Testausführung ist die Reproduzierbarkeit der Tests. Vor allem im Kontext der Automatisierung der Testausführung stößt man schnell auf Probleme, die bei einer erneuten Testausführung auftreten. Um einen Benutzer zweimal zu registrieren, muss man ihn zwischen den Testdurchläufen manuell aus der Datenbank löschen. Ebenso verhält es sich mit den meisten anderen Testfällen, die modifizierende Datenbankzugriffe beinhalten.

Um den Testfluss nicht durch manuelle Zwischenschritte zu behindern müssen diese ebenso automatisiert werden. Das Datenmodell muss vor jedem Teststart auf einen definierten Stand gebracht werden. Nach jedem Testdurchlauf muss der Datenmodellzustand wieder auf den ursprünglichen Stand zurückgesetzt werden. Dabei ist unter Datenmodell nur ein Teil des gesamten Datenbankinhalts gemeint, der von dem Test direkt betroffen/modifiziert wird. Damit wird erreicht, dass einzelne Testdurchläufe und die Bereinigung des Datenmodells danach sich nicht untereinander beeinflussen. (Die modifizierenden Tests müssen dafür auf disjunkten Teilmengen des Datenmodells arbeiten)

Die Definition des Reinigungsvorgangs nach einem Test gehört zu der Testfallbeschreibung und sieht im Groben folgendermaßen aus:

DB-Zugriffsart Reinigungsvorgang nach dem Testdurchlauf
Lesender Zugriff keiner
Elemente hinzugefügt Elemente löschen
Elemente gelöscht Elemente wiederherstellen
Elemente modifiziert Änderungen zurücksetzen
Transaktion mit unterschiedlichen Arten von DB-Zugriffen Transaktion in die elementaren Operationen aufteilen und diese Tabelle auf jede Operation anwenden

Die Realisation der Vor- und Nachbedingungen im Datenmodell kann automatisiert und in Ant integriert werden. Dafür ist das Datenbank-Testtool DBUnit bzw. seine Bibliothek sehr gut geeignet, denn sie ist Ant-kompatibel und enthält die Funktionalität zum Setzen des gewünschten Datenbankzustands.

Die Bibliothek kann unter [3] heruntergeladen werden. Die Task dbunit erlaubt das Exportieren und Vergleichen von Daten aus der Datenbank, sowie die Modifizierung der DB-Inhalte. Die Modifizierung kann auf Basis von SQL-Anweisungen oder auf Basis von XML-Beschreibungen der Daten erfolgen. Das nachfolgende Beispiel löscht alle Inhalte aus den Tabellen, die in der Datei DBImage.xml angegeben sind und fügt danach neue Inhalte aus derselben Datei ein.

<dbunit driver="${class.driver.dbms}"
   url="jdbc:mysql://${host.testmachine}/estudy"
   userid="${login.dbms}"
   password="${pass.dbms}">
      <operation
         format="xml"
         transaction="true"
         type="CLEAN_INSERT"
         src="DBImage.xml"/>
</dbunit>

Die xml-Datei, in der die Daten definiert wurden, sieht so aus:

<?xml version='1.0' encoding='UTF-8'?>
   <dataset>
      <table name="waitinglist">
         <column>Email</column>
         <column>Login</column>
         <column>Password</column>
         <column>CreationTime</column>
         <column>Data</column>
         <column>mail_sent</column>
         <row>
            <value>albert_einstein@everythingisrelative.com</value>
            <value>AlbertEinstein</value>
            <value>642c6e2823cd476cfda5cd1449adf47b</value>
            <value>1179107150</value>
            <value>a:3:{s:7:"Vorname";s:6:"Albert";s:8:"Nachname";s:8:"Einstein";s:5:"Grund";s:4:"Test";}</value>
            <value>0</value>
         </row>
      </table>
</dataset>

Man sieht hier, dass die obere DBUnit-Task die Inhalte der Tabelle mit den wartenden Benutzern löscht und danach einen wartenden Benutzer einträgt. Dieser Reinigungsprozess ist bei dem Test des Registrierungsvorgangs sehr hilfreich, da er neue Benutzer sofort nach dem Test wieder löscht und somit den alten Zustand der Tabelle wiederherstellt.

Ausführung des Registrierungstestfalls

In diesem Abschnitt werden die oben beschriebenen Konzepte am Beispiel der Ausführung des Registrierungstestfalls demonstriert. Das Ant-Skript sieht folgendermaßen aus:

   <project name="JMeter" basedir=".">
   <!--JMeter-Verzeichnis-->
   <property name="dir.jmeter" value="C:/JMeter"/>
   <!--Host der Anwendung unter Test-->
   <property name="host.testmachine" value="192.168.178.22"/>
   <!--DBMS-Benutzername-->
   <property name="login.dbms" value="root"/>
   <!--DBMS-Paswort-->
   <property name="pass.dbms" value="WebApps2007"/>
   <!--JDBC-Treiber für das DBMS-->
   <property name="class.driver.dbms" value="com.mysql.jdbc.Driver"/>
   <!--Ant-Task für die Ausführung von JMeter-->
   <taskdef
      name="jmeter"
      classname="org.programmerplanet.ant.taskdefs.jmeter.JMeterTask"/>
   <!--Ant-Task für die Ausführung von DBUnit-->
   <taskdef
      name="dbunit"
      classname="org.dbunit.ant.DbUnitTask"/>
   <target name="runtest">
   <!--Test ausführen-->
   <jmeter
      jmeterhome="${dir.jmeter}"
      testplan="${basedir}/TEST.REGISTER.jmx"
      resultlog="${basedir}/Results.xml"/>
     <!--Ergebnisse aufbereiten-->
   <xslt
      in="${basedir}/Results.xml"
      out="${basedir}/Results.html"
      style="${basedir}/Reportstyle.xsl"/>
   <!--Modellzustand bereinigen-->
   <dbunit driver="${class.driver.dbms}"
      url="jdbc:mysql://${host.testmachine}/estudy"
      userid="${login.dbms}"
      password="${pass.dbms}">
   <operation
      format="xml"
      transaction="true"
      type="CLEAN_INSERT"
      src="DBImage.xml"/>
   </dbunit>
</target>

Server-Monitoring

Zur Performance-Analyse des Testsystems wurde folgendes Shell-Skript entwickelt:

#!/bin/sh

meminfo=$(more /proc/meminfo | head -n 1)
cpuinfo=$(more /proc/cpuinfo | head -n 5 | tail -n 1)

echo
echo
echo ----------------------------------------------------------------- 
echo
echo OUTPUT-FORMAT:
echo time,r,b,swpd,free,buff,cache,si,so,bi,bo,in,cs,us,sy,id,wa,ce,sr,ss,bs,br,tc
echo
echo   Procs
echo       r: The number of processes waiting for run time.
echo       b: The number of processes in uninterruptible sleep.
echo
echo   Memory
echo       swpd: the amount of virtual memory used.
echo       free: the amount of idle memory.
echo       buff: the amount of memory used as buffers.
echo       cache: the amount of memory used as cache.
echo
echo   Swap
echo       si: Amount of memory swapped in from disk.
echo       so: Amount of memory swapped to disk.
echo
echo   IO
echo       bi: Blocks received from a block device.
echo       bo: Blocks sent to a block device.
echo
echo   System
echo       in: The number of interrupts per second, including the clock.
echo       cs: The number of context switches per second.
echo
echo   CPU
echo       us: Time spent running non-kernel code. 
echo       sy: Time spent running kernel code. 
echo       id: Time spent idle, this includes IO-wait time.
echo       wa: Time spent waiting for IO..
echo            
echo   Network
echo       ce: connections established
echo       sr: segments received
echo       ss: segments send out
echo
echo   MySql
echo       bs: bytes sent
echo       br: bytes read
echo       tc: threads connected 
echo
echo   System Info:
echo $cpuinfo
echo $meminfo
echo
echo -----------------------------------------------------------------
echo
echo
date
echo start logging...
echo
echo

while [ 1 == 1 ] #forever
do  


    mysql_outp=$(test=$(mysql -ubrowser -pbrowserteam --exec="show status;" | grep -e Bytes_sent -e Bytes_received
        -e Threads_connected | sed -e 's/[^0-9]\+/,/g');echo $test | sed -e 's/ //g')
    vmstat_outp=$(vmstat 1 2 | tail -n 1| sed -e 's/^ */,/g' | sed -e 's/ \+/,/g')
    netstat_outp=$(test=$(netstat -s -t | head -n 8 | tail -n 3 | cut -d " " -f 5 );echo $test | sed -e 's/ /,/g')

    echo $(date +%T)$vmstat_outp,$netstat_outp$mysql_outp

done			

Dieses Skript gibt zeilenweise Datensätze mit kommaseparierten Werten für folgende Bereiche aus:

  • Procs
Number of processes waiting for run time
Number of processes in uninterruptible sleep
  • Memory
Amount of virtual memory used
Amount of idle memory
Amount of memory used as buffers
Amount of memory used as cache
  • Swap
Amount of memory swapped in from disk
Amount of memory swapped to disk
  • IO
Blocks received from a block device
Blocks sent to a block device
  • System
Number of interrupts per second, including the clock
Number of context switches per second
  • CPU
Time spent running non-kernel code
Time spent running kernel code
Time spent idle, this includes IO-wait time
Time spent waiting for IO
  • Network
Connections established
Segments received
Segments send out
  • MySql
Bytes sent
Bytes read
Threads connected

Auswertung

Die Auswertung der Tests ist neben der Testerstellung der wichtigste Teil eines Softwaretests. Die Interpretation der Testergebnisse erfordert analytisches Denken und systematisches Vorgehen. Im Fall eines Lasttests besteht die größte Herausforderung darin, Zusammenhänge zwischen der Menge der Last und dem Zeitverhalten des zu testenden Systems festzustellen. Die Visualisierung der betrachteten Werte lässt diese Zusammenhänge viel besser ausfindig machen. Ein Tool, welches die Visualisierung übernimmt, kann die Testauswertung um einiges erleichtern. Die Vorgaben für ein solches Tool sind:

  • Einlesen der Testergebnisse aus zwei Quellen (Serverwerte, Testclientwerte)
  • Visuelle Darstellung der Werte (CPU-Auslastung, Speicherverbrauch, Anzahl der Kontextwechsel) des zu testenden Systems in Form von Graphendiagrammen, die auf eine Zeitachse abgebildet sind
  • Visuelle Darstellung der Last (Anzahl der Systemzugriffe differenziert nach Testfällen, gesamte Anzahl der Systemzugriffe) abgebildet auf die Zeitachse.
  • Darstellung mehrerer Graphen in einem Koordinatensystem.
  • Auswahl der Graphen, deren Darstellung gewünscht ist.
  • Synchronisation der beiden Arten der Testergebnisdiagramme.
  • Skalierung einzelner Graphen über die Y-Achse zwecks besserer Übersicht und Kurvenvergleich.
  • Weitere spezifische Funktionen wie Zooming, Speicherung, Drucken und grafische Anpassung von Graphen.

Standardwerkzeuge wie Excel oder Open Office können diese Funktionalität liefern, haben aber folgende Nachteile:

  • Sie sind sehr unflexibel, was das Eingabeformat betrifft. (Nur CSV-Format erlaubt, JMeter bietet aber auch XML-Formatierung der Ergebnisse)
  • Der Benutzer ist auf das Werkzeug angewiesen.
  • Die Werkzeuge benötigen einen für eine solche spezifische Aufgabe wie die Testauswertung einen viel zu hohen Lernaufwand.

Der größte Vorteil solcher Standardwerkzeuge ist die mächtige Funktionalität, welche die meisten Standardprobleme der Visualisierung von Zahlenreihen übernimmt.

Ein gesunder Kompromiss aus der Standardsoftware und Eigenentwicklung bietet die Java-Bibliothek JFreeChart. Sie basiert auf Swing, liefert die notwendige Funktionalität und lässt sich bequem in Java-Entwicklung einbinden. Die Bibliothek ist frei verfügbar und sehr flexibel im Einsatz. Mit ihrer Hilfe wurde das Tool ChartBuilder zur Visualisierung der Testergebnisse erstellt.

Datenformat der Testergebnisse

In der aktuellen Version versteht ChartBuilder nur das von dem Serverlogger und JMeter verwendeten Datenformat.

Die Ergebnisdateien von den beiden Tools müssen im Verzeichnis von ChartBuilder liegen und entsprechend server.csv und jmeter.jtl heißen. (Die Namen lassen sich in der Property-Datei ändern.) Beide Formate sind kommaseparierte Wertereihen, wobei die Loggerdatei noch einen Header mit Kommentaren und Erklärungen besitzt.

Logger-Datei-Format

Das Loggerdatenformat besteht aus einem Header- und einem Nutzlastteil. Der Header ist ein mehrzeiliger Text mit Erklärungen und Kommentaren, der mit einer Zeile mit dem Inhalt „Start logging…“ endet. Dieser Zeile folgt eine Leerzeile, nach welcher der Nutzlastteil anfängt.

Die Nutzlast besteht aus mindestens einer kommaseparierten Zeile. Die Anzahl der kommaseparierten Werte in jedem Datensatz muss mit der Anzahl der Y-Labels der Charts in der Propertydatei des ChartBulders im Zusammenhang stehen. (Es muss in jedem Datensatz einen Wert weniger geben als es Labels gibt)

Die Werte der Datensätze sind folgendermaßen aufgebaut: Zuerst kommt ein Datum im Format HH:mm:ss (s. Java-Dokumentation zur Klasse SimpleDateFormat). Diesem Wert folgen genau so viele positive natürliche Zahlen (inkl. 0), wie es Labels in der oben genannten Property-Datei gibt. Für jede Zahlenkolonne erstellt der ChartBuilder einen Graphen in seinem Diagramm. Die Zeitangaben sind nicht als absolute Messgrößen relevant, da nur der Relativwert zum Startdatum als X-Koordinate des Graphen verwendet wird.

Beispiel für einen Datensatz:

12:19:38,0,0,329672,1501116,9620,414204,0,0,0,0,254,70,0,0,100,0,13,10534043,11691651

JMeter-Datei-Format

ChartBuilder interpretiert die CSV-Form der JMeter-Ergebnisse. Das sind ebenfalls Dateien mit kommaseparierten Wertereihen. Sie enthalten die Namen der Requests, ihre Start- und Dauerzeiten sowie Angaben zu den zugehörigen Testfällen (JMeter Threadgruppen). Für das genaue Datenformat s. JMeter Dokumentation.

Beispiel für einen Datensatz:

1182290918694,321,C5.2 - Show Login Dialog,307,Temporary Redirect,Thread Course TestCase05.2 6-1,text,false

Benutzung

Vor dem Start des ChartBuilder müssen die Dateien server.csv und jmeter.jtl im Verzeichnis des Programms liegen. Von dort werden die Testdaten gelesen. Die Startzeiten der beiden Dateien müssen synchron sein.

Nach dem Start öffnet sich ein Fenster mit zwei Koordinatensystemen, welche über die X-Achse synchronisiert sind. Das obere Koordinatensystem zeigt die Serverwerte, das untere – die Testergebnisse des JMeter.

Durch die Auswahl der Einträge in den Listen auf der rechten Seite (Multiselect möglich) lassen sich mehrere Graphen in jedem Koordinatensystem anzeigen. Somit können sie intuitiv miteinander verglichen werden. Bei der Auswahl erscheint eine Legende, welche den Linienfarben die entsprechenden Bezeichnungen zuweist.

Die Maxima der einzelnen Kurven liegen weit auseinander (sie können sich um Millionen und Milliarden unterscheiden.) Deshalb werden bei mehrfacher Auswahl die flacheren Kurven „in den Boden gedrückt.“ Der Vergleich der Kurven fällt somit schwieriger. Um diesen Effekt zu umgehen, können einzelne Kurven um Größenordnungen skaliert werden. Dazu muss der entsprechende Button geklickt werden. (Überschrift: „Selected x 10“) Um die Originalskalierung wiederherzustellen, klicke man auf den Button mit der Überschrift „Selected 1:1“.

Die Darstellung der Kurven kann vergrößert werden, indem der Rahmen über einen Bereich in einem der Koordinatensysteme gezogen wird.

Der Klick mit der rechten Maustaste führt ein Popup-Menu zum Vorschein, welches nützliche Funktionen zum Zoomen, Speichern und Drucken von Graphendiagrammen enthält.


ChartBuilder.png


Erweiterungsmöglichkeiten

Die Wertereihen des Serverloggers können um weitere Datensatzelemente am Ende jedes Datensatzes ergänzt werden. Für jede neue Zahlenkolonne muss in der Property-Datei ein Y-Label definiert werden.

Um andere Datenformate zu unterstützen, muss die Klasse FileDataProvider ersetzt werden. So können XML-Dateien, Datenbanken oder Netzwerkstationen als Quellen der Testreihen benutzt werden.

Bei der Weiterentwicklung sind folgende Punkte wichtig:

  • Navigation im Diagramm über Koordinateneingaben (genaues Zooming und Scrolling)
  • Möglichkeit der Definition eines Zeitunterschieds zw. den Testergebnisstartzeiten, falls die Startzeiten um ein paar Sekunden (oder mehr) voneinander abweichen, und die entsprechende Versetzung der Diagramme.
  • Schnittstelle IDataProvider, die von der Klasse FileDataProvider und allen anderen Datenleserklassen implementiert wird.
  • Implementierung der Methode compact() in der Klasse ExtendedXYSeries, welche die Anzahl der Koordinaten-Punkte reduzieren soll, indem sie Punkte auf den geraden Teilkurven der Graphen auf zwei Punkte beschränkt. (Eine Gerade lässt sich über zwei Punkte definieren => großer Performance-Gewinn)
  • Optimierung der GUI.

Gefahrene Testszenarien und Ergebnisse

Vorgehensweise

[J] als Abkürzung für den Rechner, auf dem JMeter läuft; [E] für den, auf dem eStudy läuft

Test ausführen

  • [J+E] alte Log-Dateien löschen
  • [J] neues JMeter-Skript ablegen
  • [E] Datenbank auf einen bestimmten Stand bringen (Dump einspielen)
mysql -p < dump.sql
  • [E] PerfomanceAnalyzer starten
sudo nice -n -10 ./performanceAnalyzer.sh > performanceAnalyzer.log
  • [J] JMeter starten
nohup java -Xmx512m -Xms256m -jar ~/jmeter/bin/ApacheJMeter.jar -n
-t ~/lastskript/szenario/SZENARIO_Anfang_2.3.jmx -p ~/jmeter/bin/jmeter.properties 2>&1
  • [J] überprüfen, ob JMeter noch Anfragen sendet
tail -F JMeterResult/Szenario_Anfang_Summary_Report_TOTAL.csv

Nach dem Test

  • [J] JMeter beendet sich alleine
  • [E] den PerformanceAnalyzer kann man stoppen mit:
killall performanceAnalyzer.sh
  • [E] Log-Dateien packen
tar -czf performanceAnalyzer_DATUM_UHRZEIT.log.tar.gz performanceAnalyzer.log
  • [J] Log-Dateien packen
tar -czf JMeterResults_DATUM_UHRZEIT.tar.gz jmeter.log nohup.out
JMeterResult/Szenario_Anfang_Summary_Report_TOTAL.csv JMeterResult/Szenario_Anfang_Summary_Report_TOTAL.xml
  • Gepackte Log-Dateien auf PC transferieren und in neuem Ordner auspacken
  • Jetzt kann man mit XSLT und Java ChartBuilder die Daten auswerten.


Ergebnisse

Fehler beim Erstellen des Vorschaubildes: Datei fehlt
Mittlere Beanspruchung (Lastprognose)
Fehler beim Erstellen des Vorschaubildes: Datei fehlt
Zusammebruch des Servers (Stresstest)
  • SZENARIO_TEST_SEHR_LEICHT (Version 2.0)
Testlänge: 10h
Grad: Sehr Leichte Beanspruchung
Benutzer: Durchschnittliche Benutzerlast in 2006-2007
Ergebnis: Lief ohne Probleme durch, entspricht der aktuellen Portal-Last im Live-Betrieb. Die durchschnittliche Anwortzeit lag bei 2 Sekunden pro Anfrage.
  • SZENARIO_TEST_ LEICHT (Version 2.1)
Testlänge: 10h
Grad: Leichte Beanspruchung
Benutzer: Doppelte Benutzermenge relativ zu Version 2.0
Ergebnis: Die verdoppelte Benutzeranzahl wirkte sich auf den Server durch doppelten Arbeitsspeicher-Bedarf aus. Die durchschnittliche Anwortzeit stieg auf 4 Sekunden pro Anfrage.
  • SZENARIO_TEST_ MITTEL (Version 2.2)
Testlänge: 10h
Grad: Mittlere Beanspruchung
Benutzer: Fünffache Benutzermenge relativ zu Version 2.0
Typ: Testfall entsprechend der Lastprognose
Ergebnis: Lief vollständig durch; die durchschnittliche Anwortzeit stieg jedoch auf 11 Sekunden pro Anfrage.
  • SZENARIO_TEST_ SCHWER (Version 2.3)
Testlänge: 1h
Grad: Mittlere Beanspruchung
Benutzer: Fünffache Benutzermenge relativ zu Version 2.0
Last: Fünfzigfache Last (Benutzer x 5 und Zeit / 10)
Typ: Stresstest
Ergebnis: Extremer Anstieg der Antwortzeiten des Servers, ab 15 simultanen Usern im Time-Out Bereich. Der Server stand jedoch nach Abbruch des Testlaufs ohne Beeinträchtigung wieder zu Verfügung.

Fazit

Es wurde ein Instrumentarium geschaffen, umfangreiche Testläufe erstellen, durchführen und effizient auswerten zu können. Mit diesem wurden die beschriebenen Szenarien durchlaufen. Es wurden Testläufe auf dem Portal-Server, sowie auf dem Hetzner-Server durchgeführt. Die erhaltenen Ergebnisse reichen jedoch nicht aus, eine definitive Empfehlung für einen der beiden Hoster / Umgebungen aussprechen zu können.

Das Team hat ein allgemeines Vorgehensmodell zur Erstellung, Durchführung und Auswertung von Lasttests für Webportale aufgebaut und angewandt. Für eine weitergehende Analyse kann das Vorgehensmodell durch andere Teams wiederverwendet und erweitert werden. Die entstandene Dokumentation erleichtert die Einarbeitung in das Themengebiet. Die Wiederverwendung der entstandenen Komponenten spart Entwicklungszeit und es bleibt mehr Zeit für die Durchführung von Testläufen.


Anhang

Tipps und Tricks

  • Falls eine Java-Anwendung sehr viel Arbeitsspeicher braucht, kann es mit folgendem Fehler abstürzen:
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
Die Optionen
-Xmx512m -Xms256m
beheben meist dieses Problem. Dieses Problem trat bei allen Java-Anwendungen auf, die benutzt wurden (JMeter, XLSTransformation, ChartBuilder)
  • Wenn JMeter ein Passwort für den Schlüsselspeicher nachfragt, einfach mehrmals leer bestätigen.
  • Um JMeter zu starten, führt man die Batchdatei jmeter.bat aus. Falls es (z.B. bei Windows Vista) zu Problemen kommt, kann man JMeter manuell starten (mit GUI). Man wechselt in das Verzeichnis jakarta-jmeter-X.X..../bin/ und gibt folgenden Befehl ein:
    java -Xmx512m -Xms256m -jar ApacheJMeter.jar