HTTP-Requests mit JMeter und SQLite abspielen

Ich finde es immer wieder interessant, wie kreativ einige Fragestellungen lösbar sind. Ein Beispiel dafür sind die Tests für die in den Shop integrierte Recommendation Engine eines externen Herstellers. Natürlich wollen wir nicht nur wissen, ob die Integration technisch gelungen ist, sondern auch, ob die Performance und die Fachlichkeit stimmt. Zu diesem Thema hat mir Matthias aus meinem Team eine interessante Story erzählt, die ich Euch nicht vorenthalten möchte:

Mein aktuelles Spezialgebiet sind Produktempfehlungen. Damit meine ich vor allem Empfehlungen wie „Kunden interessiert auch“ oder „Wird zusammengekauft mit“. Meistens muss ich dann eine von zwei Fragen beantworten: „Warum sehe ich diese Empfehlung?“ und „Warum sehe ich diese Empfehlung nicht?“. Bei redaktionell gepflegten Empfehlungen ist das noch vergleichsweise einfach nachzuvollziehen. Schwieriger ist das bei Empfehlungen, die auf dem Klick- und Kaufverhalten der Kunden basieren. Aber auch das ist möglich.
Eine Lösungsvariante habe ich im folgenden beschrieben. Ich hab das Beispiel allerdings so angepasst, dass es einfach nachvollziehbar und vielleicht auch für eigene Fragestellungen benutzbar ist.

Die Anforderung

Es gilt mit Hilfe eines Requestlogs den Klickverlauf auf der Webseite möglichst originalgetreu nachzustellen. Für die verschiedenen Szenarien mussten daher die passenden http-Requests aus den Logfiles für verschiedene Zeiträume extrahiert werden und möglichst in der gleichen zeitlichen Folge wieder gegen ein Testsystem abgespielt werden.

Die Tools

Bei den verwendeten Werkzeugen fiel meine Wahl auf JMeter und SQLite. Beide Tools sind frei verfügbar, flexibel und sehr performant. Darüber hinaus kann man mit JMeter sehr einfach die Abspielgeschwindigkeit anpassen. Mit SQLite hingegen lassen sich auch aus einem größeren Datenbestand mittels SQL die Daten für die Testszenarien selektieren. Die Selektion lässt sich dann bei Bedarf mit wenig Aufwand anpassen. Ein Bonus ist die Unterstützung mehrerer Plattformen durch beide Tools. Somit kann man die Lösung z.B. auf dem Windows Arbeitsplatz einrichten und dann auf die Unix Testumgebung ausrollen.

Das Beispiel

Im folgenden möchte ich ein vereinfachtes Beispiel zum selber ausprobieren beschreiben.

Als erstes muss man sich die nötige Software installieren. Das sind jmeter (ich habe Version 2.9 verwendet), sqlite (Version 3.7) sowie ein JDBC-Treiber für SQLite. Der JDBC-Treiber sollte dabei am besten in das lib-Verzeichnis von JMeter kopiert werden.

Für unser Beispiel habe ich ein paar Testdaten vorbereitet:

-- create table for sample data
create table product_sample_data (request_time time, product_id varchar);
-- table with sample data
insert into product_sample_data (request_time, product_id) values ('12:00:00','AKL10021480877');
insert into product_sample_data (request_time, product_id) values ('12:00:01','AKL10018757012');
insert into product_sample_data (request_time, product_id) values ('12:00:01','AKL5976077');
insert into product_sample_data (request_time, product_id) values ('12:00:02','AKL10006882469');

Diese Daten müssen nun in ein File namens sample_data.sql in das Verzeichnis von SQLite gespeichert werden.

Mit dem Aufruf >sqlite3 sample_db.sqlite < sample_data.sql< erstellt man damit eine neue Datenbank. In den hier abgebildeten Screenshots des Beispiels liegt die Datenbank unter c:\dev\blog.

Nun kommt der JMeter-Testplan an die Reihe. Er beinhaltet folgende Elemente:

  1. Man beginnt am besten mit dem Konfigurations-Element JDBC Connection Configuration. In dieses muss man lediglich einen Namen für die Poolvariable vergeben sowie die URL der Datenbank und die JDBC-Treiberklasse eintragen. Die Poolvariable stellt die Verbindung zwischen Samplern und Konfiguration her.
    jdbc_config
  2. Für das nachzustellende Szenario legt man nun eine Thread-Gruppe an. Da die Beispieldaten einen Zeitraum von 3 Sekunden abdecken, sollte der Thread dreimal wiederholt werden. so kann man jeweils die Daten auslesen, die innerhalb einer bestimmten Sekunde angefallen sind.
    thread_group
  3. Als nächstes folgt ein Zähler für die Durchläufe. In diesem einfachen Beispiel könnte er auch entfallen, indem man direkt den Thread auswertet. Aber spätestens wenn mehrere Threads zum Einsatz kommen, braucht man den Zähler wieder.
    time_counter
  4. Es folgt ein einfacher Controller, der die Datenbankzugriffe und die Steuerung der Abspielgeschwindigkeit zusammenfasst.
  5. Unter diesem Controller ist der Sampler für die Datenbankabfragen zu finden.
    jdbc_request
    Mit der Poolvariablen binden wir ihn an die Konfiguration aus Punkt 1. Unser SQL-Statement liefert uns alle Requests für den Zeitraum von 1 Sekunde. Mit der Hilfe von etwas Javascript hilft uns unser in Schritt 3 eingefügter Zähler bei der Auswahl der richtigen Sekunde. Unsere Zielvariable benennen wir analog zur Spalte in der Datenbank. JMeter macht dann daraus ein Array.
  6. Mit einem Konstanten Durchsatz-Timer steuern wir schließlich die Abspielgeschwindigkeit.
    throughput_timer
    Wenn wir in unserem Beipiel einen Durchsatz von 120 an Stelle von 60 angeben würden, dann würden wir die Requests über den Zeitraum von zwei Minuten in einer Minute abfragen (und auch abspielen) und somit unsere Abspielgeschwindigkeit verdoppeln.
  7. Zum Abspielen der HTTP-Requests benötigen wir einen Schleifen-Controller, da die Anzahl der abzurufenden Request ja pro Sekunde variabel ist.
    loop_controller
  8. Die Anzahl der Durchläufe wird durch die Größe unseres Datenarrays bestimmt. Diese kann man abrufen, indem man den Variablennamen um _# ergänzt.
  9. Um die Requests einzeln abzurufen verwenden wir wieder einen Zähler.
    request_counter
    Die Arraygröße unserer Datenvariablen gibt dabei den maximalen Zählerstand vor.
  10. Fast haben wir es geschafft. Auf jeden Fall brauchen wir noch einen Sampler für die http-Requests.
    http_request
    Mit der Funktion __V() können wir gezielt Elemente unseres Datenarrays abfragen.
  11. Um unseren Testplan zu prüfen, ziehen wir noch einen View Results Tree Listener in die Thread-Gruppe.
    result_tree
    Da sehen wir dann auch die fertig generierte SQL Anweisung. Wenn größere Datenmengen zum Einsatz kommen, muss diesser Listener unbedingt entfernt werden, da er erheblichen Einfluss auf Speicherverbrauch und Performance von JMeter hat!

Das war’s dann. Wenn man den Testplan nun ausführt, werden die Requests über die Dauer von 3 Sekunden auch im Sekundentakt ausgeführt.
Übrigens: Wenn ihr das nicht alles selbst zusammenklicken wollt, findet sich hier die JMeter-Datei sowie die erzeugte Datenbank.