The Grinder
e' un programma Open Source per
effettuare test di carico
e per misurare i tempi di risposta dei programmi
rivolto principalmente, ma non solo, ad applicazioni web.
E' un ottimo prodotto distribuito gratuitamente con i
sorgenti con una licenze BSD style.
E' realizzato in Java con librerie standard, quindi eseguibile virtualmente
su qualsiasi sistema.
I test vengono generati/programmati con il linguaggio Jython,
l'implementazione Java di Python, quindi sono facilmente modificabili
ed adattabili a casi anche molto complessi.
Anche se i test effettuabili con Grinder possono
venir programmati a piacere, il modo piu' semplice
per utilizzarli su un sito web e' quello di registrare una
sessione di lavoro di un utente e quindi replicarla per centinaia
o migliaia di utenti.
In questo breve documento vedremo come utilizzare il Grinder
soprattutto in questo modo.
In realta' il Grinder e' uno strumento molto completo ed,
oltre ad una gestione molto completa dell'HTTP (eg.
cookies, SSL) puo' essere sfruttato nel test di applicazioni
complesse (eg. SOAP). Personalmente l'ho utilizzato molto per
effettuare benchmark verso database relazionali acceduti via JDBC...
I componenti principali del Grinder sono tre:
- Console: l'interfaccia di controllo e monitoraggio
dei test svolti.
- Agent: il programma che si occupa di eseguire gli script di test
il numero stabilito di volte simulando il carico.
Gli Agent possono essere attivati su sistemi differenti e
vengono coordinati dalla Console centrale.
- TCP Proxy: un proxy server che raccoglie
le richieste inviate dal browser del client via HTTP/HTTPS
e le salva in uno script in formato Jython.
Questo documento fa riferimento alla versione 3 (Production),
le ultime novita' sono riportate in questo capitolo
(NdE aggiornato alla versione 3.4 rilasciata in Aprile 2010).
Come introduzione basta cosi'. Ora iniziamo a lavorare!
Installazione
L'installazione e' molto semplice. Scaricato il software
(in formato .ZIP),
dal sito ufficiale
e' sufficiente decomprimerlo (eg. in C:\grinder3\).
Basta:
l'installazione e' terminata!
Si tratta di programmi 100% Pure Java e' quindi necessario che sul sistema
ospite sia installato il JRE (Java Runtime Environment).
E' consigliabile la versione 1.4 o successive (puo' cambiare nel tempo).
Per rendere piu' semplice il lancio dei programmi e'
utile settare il CLASSPATH in modo che raggiunga
le librerie di Grinder.
Nello strano mondo di Windows si configura su
Pannello di controllo - Sistema - Avanzate - Variabili d'ambiente
CLASSPATH=%CLASSPATH%;C:\grinder3\lib\grinder.jar
Quanto riportato e' un esempio,
naturalmente i path dipendono dalle versioni (eg. sul alcune versioni precedenti
era necessario inserire jython.jar) e dalle directory scelte
per l'installazione del software...
Naturalmente ciascuno puo' effettuare la configurazione come preferisce,
per quello che mi riguarda su PC trovo comodo utilizzare una directory specifica
per il lancio delle applicazioni (eg. \test_grinder) e creare qualche icona sul desktop
associata a semplici script (eg. agent.bat):
set CLASSPATH=%CLASSPATH%;C:\grinder3\lib\grinder.jar;C:\grinder3\lib\mysql-connector-java-5.0.4-bin.jar;C:\grinder3\lib\ojdbc14.jar
cd \test_grinder
java net.grinder.Grinder
Grinder e' un programma Java 100%, quindi gira praticamente ovunque.
Tuttavia se lo utilizzate su un benchmark impegnativo installatelo su
un sistema o su una batteria di sistemi sufficientemente potenti e
vicini vicini ai server in esame.
Ovviamente sistemi Unix/Linux vanno benissimo, anzi meglio!
PATH=$PATH:/opt/java1.5/bin
CLASSPATH=$CLASSPATH:/home/grinder3/lib/grinder.jar
export CLASSPATH
cd /home/test_grinder
java net.grinder.Grinder
Registrazione percorsi
Il modo piu' semplice per definire uno scenario di utilizzo
di un'applicazione web e' quello di registrare la navigazione
di un utente sul browser.
Per fare questo e' sufficiente attivare il programma proxy di Grinder,
modificare la configurazione del browser per farlo puntare al proxy
e registrare una normale navigazione dell'utente.
-
Per attivare il proxy lanciare:
java net.grinder.TCPProxy -console -http > grinder.py
Nota: sono disponibili diverse modalita' di registrazione.
Nelle precedenti versioni si utilizzava il comando:
java net.grinder.TCPProxy -console -httpPlugin
-
La configurazione del browser dipende dal browser utilizzato...
Ad esempio:
In ogni modo basta far puntare il proxy a localhost sulla porta 8001.
-
A questo punto basta navigare sulle pagine che si intende testare!
Navigate alla stessa velocita' che usereste normalmente: il proxy
registra tutti i tempi di attesa per simulare gli utenti reali.
Quanto riportato e' sufficiente per registrare un percorso su una
applicazione locale. Il TCP Proxy ha diverse opzioni che consentono
di utilizzare porte differenti per protocollo, utilizzare un ulteriore
Proxy Server, ... che consentono di
registrare il percorsi anche nei casi piu' complessi.
La navigazione e' salvata sul file di output file.py.
Il file e' uno script Jython (l'implementazione Java
del diffuso linguaggio di scripting Python)
che contiene tutti i dettagli della sessione HTTP.
Nelle versioni
precedenti erano due file:
httpscript.py e
httpscript_tests.py.
Console
Il lancio dei test avviene generalmente coordinando le diverse
attivita' e monitorando i risultati da una console grafica.
Per attivare la Console lanciare:
java net.grinder.Console
Le funzionalita' principali della Console sono:
- la distribuzione degli script di test agli agenti
- l'attivazione dei test
- la raccolta dei risultati dell'esecuzione dei test
in forma grafica e come risultato tabellare
Usare l'interfaccia grafica e' piu' semplice che spiegarla...
Nelle versioni precedenti per attivare la Console nazionalizzata in italiano
occorreva lanciare il comando:
java -Duser.language="it" net.grinder.Console
Ora la selezione del linguaggio e' automatica, ma e' sempre possibile cambiare
il linguaggio di default con la sintassi indicata.
Naturalmente, prima di poter vedere i grafici prestazionali e'
necessario attivare gli Agent!
Agent
Eseguire il test e' il compito dell'Agent Grinder.
Per attivare l'agente il comando e':
java net.grinder.Grinder
Una volta attivato l'agente questo attende i comandi dalla Console ed
esegue o, per essere piu' precisi, fa eseguire i test richiesti.
Per descrivere in modo compiuto le possibilita' del Grinder
abbiamo bisogno di un po' di terminologia...
Gli script Jython contengono gli scenari ovvero i percorsi di test.
Questi possono essere stati registrati con il TCPProxy, come abbiamo visto prima,
o programmati con un normale editor.
Una singola esecuzione di un percorso di test viene chiamata run.
Le misure prestazionali sono raccolte misurando i tempi di esecuzione
di ogni run.
Una sessione di test utilizza piu' run eseguiti in sequenza.
I run sono eseguiti da thread in parallelo.
Il numero massimo di thread utilizzabili e' limitato
dalla memoria della Java Virtual Machine (JVM) che li ospita.
E' possibile utilizzare piu' JVM
chiamati Worker Process.
L'Agent Grinder coordina le attivita' dei vari Worker Process
sul sistema e comunica con la Console.
Il carico che un agente riesce a produrre e' tipicamente limitato dalla CPU
del sistema che lo ospita o dalla capacita' della connessione in rete.
La Console puo' essere locale (localhost di default) oppure ospitata
su un altro sistema in rete.
E' cosi' possibile coordinare dalla Console agenti che operano su sistemi
diversi utilizzando una batteria di sistemi che eseguono il test.
Il Think Time, che e' stato registrato durante la registrazione
degli scenari, viene tipicamente reso variabile secondo la distribuzione
normale di cui si puo' impostare la devizione standard.
Oppure puo' essere ridotto a piacere (anche annullato) per effettuare
uno stress test.
La fase di attivazione dei processi e dei thread viene chiamata
Ramp-up e viene generalmente esclusa dalle statistiche poiche'
e' una fase necessaria porre il sistema sotto test con un carico a regime.
Le attivazioni dei processi e dei thread possono essere scalate nel tempo
secondo una distribuzione random uniforme.
Tutte queste scelte sono parametrizzate nel file XXX.properties
associato allo script da lanciare.
Un file di esempio e' il seguente:
# Process:
# Console -> Agent -> Worker Process (JVM) -> Thread -> Run
grinder.processes=5
grinder.threads=10
grinder.runs=60
### Timing: distribuzione dei tempi di think
# Uniforme sull'initial e con distribuzione normale sugli sleep
grinder.initialSleepTime=100
grinder.sleepTimeFactor=0
# grinder.sleepTimeVariation=0.2
### Ramp up: partenza graduale dei processi
grinder.initialProcesses=1
grinder.processIncrement=1
grinder.processIncrementInterval=1000
### Log
grinder.logDirectory=log
grinder.numberOfOldLogs=2
### Console:
# grinder.consoleHost=localhost
# grinder.script=grinder.py
grinder.useConsole=true
grinder.script=myScript
Nell'esempio di configurazione vengono eseguite 50 sessioni utenti contemporanee
(5 processi x 10 thread) che eseguono lo script scelto per 60 volte (se vale 0
per sempre)
senza tempi di pensamento per ogni Agent attivo.
Monitorando i sistemi sotto test e' possibile rilevare il carico
cosi' generato.
I risultati raccolti dalla Console possono sono salvati su un file di testo
(delimitato da TAB, quindi immediatamente caricabile su uno Spreadsheet).
I dati raccolti sono molto completi e consentono di identificare immediatamente
le transazioni di maggior durata.
Quando deve essere simulato un carico significativo e' necessario
utilizzare una batteria di Agent che agiscono in parallelo.
Ovviamente gli Agent vanno collocati in rete "il piu' vicino possibile"
ai sistemi da esaminare. In questo caso la configurazione degli agent remoti
richiede due parti: lo script di lancio ed il file grinder.properties.
Ecco una configurazione d'esempio:
# Agent.sh script
# mysql-connector-java-5.1.14-bin.jar ojdbc6.jar postgresql-9.0-801.jdbc4.jar
CLASSPATH=$CLASSPATH:/bench/grinder-3.4/lib/grinder.jar:/bench/postgresql-9.0-801.jdbc4.jar
export CLASSPATH
cd /bench
java net.grinder.Grinder
# grinder.properties for remote agent
grinder.consoleHost=10.0.0.1
grinder.useConsole=true
Dove ovviamente l'IP indicato
deve essere quello del sistema su cui gira la console del Grinder.
Nella configurazione dei test e' importante controllare il parametro grinder.consoleHost:
quando si utilizzano Agent remoti non va usato il valore localhost ma l'IP della console.
Ovviamente il sistema su cui opera la console deve essere raggiungibile dagli Agent.
La porta utilizzata (per un eventuale apertura del firewall) e' la 6372.
Script
I run sono costituiti da script Python.
E' quindi possibile programmare test complessi a piacere,
tuttavia un modo semplice per iniziare e' quello di utilizzare
gli script registrati con il TCPProxy modificandoli secondo
le necessita'.
I file registrati sono
di semplice comprensione poiche' corrispondenti all'attivita'
di un utente. Una volta terminata la registrazione e' possibile modificarlo
ritoccando ad esempio tempi di pensamento (anche se
generalmente si utilizza il parametro grinder.sleepTimeFactor)
o le URL richieste.
Una nota: a seconda delle versioni del plugin il
formato ed il numero di file
utilizzati puo' cambiare... ma la logica resta sempre la stessa.
etc
Le indicazioni riportate fino ad ora dovrebbero consentire
l'esecuzione della maggior parte dei test.
Per esigenze piu' complesse e' opportuno dare un'occhiata
alla documentazione in linea sul
sito ufficiale.
Molto utili sono anche gli esempi che vengono distribuiti con i sorgenti.
Nel seguito e' riportata, in modo non strutturato,
qualche indicazione che puo' essere utile
in qualche caso particolare...
Eseguire un test sulle prestazioni e' come andare da un
oracolo. La cosa piu' importante non sono le risposte che
ci da' ma le domande che gli facciamo!
Sembra una banalita' filosofica ma prima di analizzare al
millesimo di secondo i risultati ottenuti su un complesso
scenario eseguito da migliaia di utenti... e' meglio prima
capire a cosa servono il sistema e le applicazioni,
comprendere l'architettura dei vari componenti e
realizzare cosi' un test significativo!
Quindi attenzione alle domande che si fanno:
cosa vogliamo veramente controllare?
Anche l'interpretazione dei risultati e' importante.
Spesso le indicazioni del thoughput e del tempo medio
di servizio vanno interpretate con la dovuta cautela
per non giungere a conclusioni errate.
Per un test di carico significativo e' necessario disporre di
piu' macchine su cui lanciare gli agenti.
Altrettanto importanti sono le
connessioni di rete che debbono essere ad alta velocita'
e connesse direttamente, o il piu' direttamente possibile, ai
sistemi che si vogliono misurare.
Quando si effettuano i test e' opportuno (leggi necessario) monitorare
il comportamento dei sistemi coinvolti con gli strumenti
di controllo delle performance.
Questo vale sia per i sistemi
che ospitano le applicazioni sotto test
(eg. web server, application server, DB Server),
che per i sistemi su cui
viene generato il carico
(eg. le stazioni che ospitano gli agenti e la Console di Grinder),
che per eventuali sistemi di supporto
(eg. proxy server, load balancer, ...).
Programmare scenari multipli e' semplice con gli script Jython.
Se abbiamo due script funzionanti j1.py e j2.py il seguente
script realizza uno scenario in cui il 25% degli utenti esegue
il primo test ed il 75% il secondo (con un numero di thread multiplo di 4
per Worker Process). L'unica avvertenza, se vogliamo raccogliere
i risultati delle transazioni in modo disgiunto, e' quella di
definire numeri di transazione differenti (nelle versioni piu' recenti
e' possibile impostare il numero di partenza quando si effettuano le
registrazioni).
from net.grinder.script.Grinder import grinder
scripts = ["j1", "j2"]
for script in scripts: exec("import %s" % script)
def createTestRunner(script):
exec("x = %s.TestRunner()" % script)
return x
class TestRunner:
def __init__(self):
tid = grinder.threadID
if tid % 4 == 0:
self.testRunner = createTestRunner(scripts[0])
else:
self.testRunner = createTestRunner(scripts[1])
def __call__(self):
self.testRunner()
Solo un poco piu' complessi sono gli esempi di programmazione per
effettuare test su DB con connessioni JDBC:
Benchmark EMP7,
Benchmark TPC-B.
Oltre alla documentazione sul sito ufficiale ho trovato molto
chiaro e semplice questo
articolo.
Grinder e' un ottimo
tool Open Source per la valutazione delle prestazioni di siti web
e per effettuare test di carico.
Alternative sono prodotti commerciali come Load Runner:
probabilmente il miglior prodotto commerciale per effettuare stress/load test
distribuito da Mercury/HP)...
Oppure la creazione di semplici programmi di test in C, SQL, shell,
lynx (un browser testuale che e' possibile utilizzare da script), wget, ...
Ecco un semplice esempio con lynx:
#!/bin/sh
for i in 1 2 3 4 5 6 7 8 9 10
do
for j in 1 2 3 4 5 6 7 8 9 10
do
lynx -cache=0 -dump -accept_all_cookies -reload \
meotec.da.ru:80/index.htm > /dev/null
done
done
Ma la flessibilita' e facilita' di utilizzo di Grinder
lo rendono utilissimo e presenta diversi vantaggi rispetto alle
alternative.
Novita'
Grinder e' disponibile da anni come Beta.
In realta' la qualita' del programma e' ottima ed e' stato
perfettamente adatto allo scopo fin dalle prime versioni.
A gennaio 2008 e' uscita la versione 3 definita come prima versione di "produzione" che
ha introdotto una novita'. Quando si effettuano i test con la
Console, anziche' selezionare uno script Jython da far eseguire
agli Agent, deve essere selezionato un file properties.
Il file properties dovra' richiamare uno script valorizzando
la proprieta' grinder.script.
In questo modo il controllo della Console sugli Agent
e' molto maggiore. Non solo la Console definisce lo
script da eseguire e raccoglie i risultati,
ma intervenendo
centralmente su un unico file si definiscono il numero
di processi, di thread, di esecuzioni, il timing, ...
di tutti gli Agent coordinati dalla Console.
Altre utili funzionalita' sono la configurazione di un editor esterno
e l'utilizzo del locale dell'utente per le impostazioni sull'I18N...
Il Grinder e' un progetto "vivo" e viene periodicamente aggiornato:
conviene scaricare sempre la versione piu' aggiornata da SourceForge.
Ho trovato questo recente
articolo
che presenta le differenze tra JMeter, Gatling e Grinder in modo molto preciso
ed imparziale...
Glossario
Nello strano mondo della simulazione, dell'analisi delle prestazioni,
dello stress test, ...
si utilizzano una serie di termini che e' importante riconoscere:
- Transaction:
Transazione. Operazione utile svolta sul sistema che
si vuole misurare e rendere il piu' efficiente possibile.
Il termine e' anche utilizzato per indicare un'insieme di operazioni
che debbono essere eseguite in modo
atomico (non significa che esplodono ma che non possono
essere separate). Ma negli stress test si utilizza
il termine in senso piu' generale riferendolo anche a
selezioni di dati o a richieste HTTP.
- Throughtput:
Rappresenta la quantita' di lavoro svolta da un sistema.
Si misura in TPS.
- TPS:
Transazioni per secondo.
Numero di esecuzioni che un sistema riesce a svolgere in un secondo.
- Think time:
Tempo di pensamento. E' i tempo che l'utente impiega a
valutare la risposta del sistema prima di sottoporre la
transazione successiva.
Ovvero il tempo in cui l'utente non
fa nulla (se pensa veramente non e' dato di saperlo,
personalmente ho qualche dubbio).
Abbassando il think time si rendono gli utenti "piu veloci".
Con un pensamento a 0 si simula un utente velocissimo che,
appena riceve una risposta dal sistema, sottomette
immediatamente la richiesta sucessiva.
- Mean time:
Si tratta del tempo medio di servizio
ovvero il tempo in cui un determinato servizio restituisce la risposta
all'utente. A volte e' possibile determinarne le componenti quali
il Service time (tempo effettivamente utilizzato da sistema per erogare il
servizio richiesto) e Wait time (tempo "perso" in coda in attesa che la
risorsa richiesta si liberi dalle altre attivita' in corso).
- Bottleneck analisys:
e' l'analisi dei colli di bottiglia. Non e' la scienza degli ubriaconi...
ma cerca di determinare i punti critici del sistema che ne limitano
le prestazioni.
- Performance:
significa prestazioni e' non sempre facile renderle ottimali.
Tipicamente si cerca di migliorare il Throughtput ovvero
la quantita' di lavoro svolta da un sistema, oppure
si vogliono ridurre i tempi di risposta delle applicazioni,
oppure raggiungere il massimo numero di utenti con tempi di
risposta accettabili
... spesso gli obiettivi sono in contrasto tra loro
ed e' necessario fare delle scelte su cosa e come ottimizzare
(tuning) un sistema.
- Test:
indica in generale un'attivita' di prova di prestazioni su un sistema.
Ve ne sono di diversi tipi...
Load Test e' un test di carico in
cui vengono simulate le normali condizioni dei sistemi.
Stress test e' una prova in cui si carica
un sistema con un numero di transazioni piu' elevato
del normale per studiarne i limiti
ed il comportamento.
- Benchmark:
si tratta di un test per misurare le prestazioni di un sistema
con un applicazione standard operante su un ambiente ben definito.