
Nel mondo dello sviluppo software e dei sistemi distribuiti, garantire prestazioni elevate e stabilità sotto carichi intensi è una sfida sempre più cruciale.
Prima che un’applicazione venga rilasciata in produzione è fondamentale comprendere come reagisce in condizioni di stress: quanti utenti simultanei può gestire? Qual è il tempo di risposta medio? Quali sono i colli di bottiglia che potrebbero compromettere l’esperienza dell’utente finale?
In questo articolo vedremo come implementare un ambiente di stress test utilizzando Apache JMeter uno degli strumenti open source più diffusi per il performance testing affiancato da una dashboard web-based per la visualizzazione in tempo reale dei risultati.
Attraverso questa integrazione sarà possibile non solo simulare carichi realistici su servizi e applicazioni, ma anche monitorare metriche chiave come throughput, tempi di risposta e tassi di errore in modo intuitivo e centralizzato.
Nell’esempio sotto riportato andremo ad eseguire uno stresstest dati host/IP, PORTA ed eventuale CONTEXT ROOT
INSTALLAZIONE DI JAVA
Procedere con l’installazione di Java con il comando:
|
0 |
apt install openjdk-17-jdk wget tar -y |
INSTALLAZIONE DI NGINX
Installare Nginx con i seguenti comandi:
|
0 1 2 3 4 5 |
sudo apt install nginx -y sudo systemctl enable nginx sudo systemctl start nginx sudo mkdir -p /var/www/html/reports sudo touch /var/www/html/reports/report_index.txt sudo chown -R $USER:$USER /var/www/html/reports |
INSTALLAZIONE DI APACHE JMETER
Prima di procedere al download di Apache JMeter verificare l’ultima versione stabile disponibile al seguente link:
https://jmeter.apache.org/download_jmeter.cgi
ATTENZIONE: durante la stesura del seguente articolo l’ultima versione stabile disponibile è la 5.6.3
Scaricare Apache Jmeter con il comando:
|
0 |
wget https://downloads.apache.org/jmeter/binaries/apache-jmeter-5.6.3.tgz -P /opt/ |
Posizionarsi nella cartella /opt con il comando:
|
0 |
cd /opt |
Scompattare il .tar con il comando:
|
0 |
sudo tar -xvzf apache-jmeter-5.6.3.tgz |
Spostare tutto nella cartella /opt con il comando:
|
0 |
sudo mv apache-jmeter-5.6.3 jmeter |
Configurare il path con il comando:
|
0 1 |
echo 'export PATH=$PATH:/opt/jmeter/bin' >> ~/.bashrc source ~/.bashrc |
Posizionarsi nella cartella /opt/jmeter/ con il comando:
|
0 |
cd /opt/jmeter/ |
Creare il file test.jmx inserendo il contenuto elencato di seguito:
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
# Creare il file test.jmx (cambiare "INSERIRE L'IP SU CUI ESEGUIRE IL TEST", "INSERIRE PORTA", "INSERIRE EVENTUALE CONTEXT ROOT o LASCIARE /") #################################### <?xml version="1.0" encoding="UTF-8"?> <jmeterTestPlan version="1.2" properties="5.0" jmeter="5.6.3"> <hashTree> <TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="Tomcat Stress Test CLI" enabled="true"> <stringProp name="TestPlan.comments"></stringProp> <boolProp name="TestPlan.functional_mode">false</boolProp> <boolProp name="TestPlan.serialize_threadgroups">false</boolProp> <elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true"> <collectionProp name="Arguments.arguments"> <elementProp name="TARGET_HOST" elementType="Argument"> <stringProp name="Argument.name">TARGET_HOST</stringProp> <stringProp name="Argument.value">INSERIRE L'IP SU CUI ESEGUIRE IL TEST</stringProp> <stringProp name="Argument.metadata">=</stringProp> </elementProp> <elementProp name="TARGET_PORT" elementType="Argument"> <stringProp name="Argument.name">TARGET_PORT</stringProp> <stringProp name="Argument.value">INSERIRE PORTA</stringProp> <stringProp name="Argument.metadata">=</stringProp> </elementProp> <elementProp name="TARGET_PATH" elementType="Argument"> <stringProp name="Argument.name">TARGET_PATH</stringProp> <stringProp name="Argument.value">INSERIRE EVENTALE CONTEXT ROOT o LASCIARE /</stringProp> <stringProp name="Argument.metadata">=</stringProp> </elementProp> </collectionProp> </elementProp> <stringProp name="TestPlan.user_define_classpath"></stringProp> </TestPlan> <hashTree> <!-- Thread Group --> <ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="Stress Thread Group" enabled="true"> <stringProp name="ThreadGroup.on_sample_error">continue</stringProp> <elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Main Loop Controller" enabled="true"> <boolProp name="LoopController.continue_forever">false</boolProp> <intProp name="LoopController.loops">LOOP_COUNT_PLACEHOLDER</intProp> </elementProp> <intProp name="ThreadGroup.num_threads">NUM_THREADS_PLACEHOLDER</intProp> <intProp name="ThreadGroup.ramp_time">10</intProp> </ThreadGroup> <hashTree> <!-- HTTP Request --> <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="HTTP Request" enabled="true"> <elementProp name="HTTPsampler.Arguments" elementType="Arguments"> <collectionProp name="Arguments.arguments"/> </elementProp> <stringProp name="HTTPSampler.protocol">http</stringProp> <stringProp name="HTTPSampler.domain">${TARGET_HOST}</stringProp> <stringProp name="HTTPSampler.port">${TARGET_PORT}</stringProp> <stringProp name="HTTPSampler.path">${TARGET_PATH}</stringProp> <stringProp name="HTTPSampler.method">GET</stringProp> <boolProp name="HTTPSampler.follow_redirects">true</boolProp> </HTTPSamplerProxy> <hashTree/> </hashTree> </hashTree> </hashTree> </jmeterTestPlan> ################################### |
CREAZIONE DELLO SCRIPT PER L’ESECUZIONE DEL TEST
Con questo script bisogna passare come argomenti il numero di Threads e di Loop da eseguire.
Ad esempio passando 50 Thread e 20 Loop significa 50×20=1000
Procediamo alla creazione dello script run_jmeter.sh e incolliamo all’interno il seguente output:
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
# Creare il file run_jmeter.sh #################################### run_jmeter.sh #!/bin/bash if [ -z "$1" ] || [ -z "$2" ] || [ -z "$3" ]; then echo "Uso: $0 /percorso/del/test.jmx NUM_THREADS LOOP_COUNT" echo "Esempio: $0 /opt/jmeter/test.jmx 50 20" exit 1 fi JMX_TEMPLATE="$1" NUM_THREADS="$2" LOOP_COUNT="$3" # Controlla che il file esista if [ ! -f "$JMX_TEMPLATE" ]; then echo "❌ File non trovato: $JMX_TEMPLATE" exit 1 fi JMETER_BIN="/opt/jmeter/bin/jmeter" # Percorso JMeter REPORT_BASE="/var/www/html/reports" # Base dei report INDEX_FILE="$REPORT_BASE/report_index.txt" TIMESTAMP=$(date +"%Y%m%d_%H%M%S") REPORT_DIR="$REPORT_BASE/test_$TIMESTAMP" mkdir -p "$REPORT_DIR" TMP_JMX=$(mktemp /tmp/jmeter_test.XXXX.jmx) cp "$JMX_TEMPLATE" "$TMP_JMX" sed -i "s/NUM_THREADS_PLACEHOLDER/$NUM_THREADS/g" "$TMP_JMX" sed -i "s/LOOP_COUNT_PLACEHOLDER/$LOOP_COUNT/g" "$TMP_JMX" echo " Esecuzione JMeter: $NUM_THREADS thread × $LOOP_COUNT loop (Ramp-Up automatico)..." $JMETER_BIN -n -t "$TMP_JMX" -l "$REPORT_DIR/results.jtl" -e -o "$REPORT_DIR/html_report" if [ $? -ne 0 ]; then echo "⚠️ JMeter ha restituito un errore. Controlla $REPORT_DIR/results.jtl" exit 1 fi REPORT_URL="http://localhost/reports/test_$TIMESTAMP/html_report/" echo "$TIMESTAMP -> $REPORT_URL (Thread=$NUM_THREADS, Loop=$LOOP_COUNT)" >> "$INDEX_FILE" echo "✅ Test completato!" echo "Report disponibile qui: $REPORT_URL" echo "Indice completo: http://localhost/reports/report_index.txt" #################################### |
Assegnare i permessi di esecuzione dello script quindi eseguirlo con i comandi:
|
0 1 |
chmod +x run_jmeter.sh ./run_jmeter.sh test.jmx 50 20 |
E’ possibile configurare un test per passare un ciclo di Threads.
Nell’ esempio sotto riportato viene eseguito un test progressivo per 100, 200, 500, 1000, 2000, 3000, 4000 e 5000 connessioni
Creare lo script run_progressive_stress.sh ed incollare l’output seguente:
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
#Creare /opt/jmeter/run_progressive_stress.sh #################################### #!/bin/bash if [ -z "$1" ]; then echo "Uso: $0 /percorso/del/template_jmx" exit 1 fi JMX_TEMPLATE="$1" if [ ! -f "$JMX_TEMPLATE" ]; then echo "❌ File non trovato: $JMX_TEMPLATE" exit 1 fi JMETER_BIN="/opt/jmeter/bin/jmeter" # Percorso JMeter REPORT_BASE="/var/www/html/reports" # Base dei report INDEX_FILE="$REPORT_BASE/report_index.txt" THREADS_LIST=(10 20 50 100 200 300 400 500) LOOPS_PER_THREAD=10 MAX_SUCCESS_THREADS=0 for THREADS in "${THREADS_LIST[@]}"; do TIMESTAMP=$(date +"%Y%m%d_%H%M%S") REPORT_DIR="$REPORT_BASE/test_${THREADS}_threads_$TIMESTAMP" mkdir -p "$REPORT_DIR" TMP_JMX=$(mktemp /tmp/jmeter_test.XXXX.jmx) cp "$JMX_TEMPLATE" "$TMP_JMX" sed -i "s/NUM_THREADS_PLACEHOLDER/$THREADS/g" "$TMP_JMX" sed -i "s/LOOP_COUNT_PLACEHOLDER/$LOOPS_PER_THREAD/g" "$TMP_JMX" echo " Esecuzione test: $THREADS thread × $LOOPS_PER_THREAD loop..." $JMETER_BIN -n -t "$TMP_JMX" -l "$REPORT_DIR/results.jtl" -e -o "$REPORT_DIR/html_report" EXIT_CODE=$? REPORT_URL="http://localhost/reports/test_${THREADS}_threads_$TIMESTAMP/html_report/" echo "$TIMESTAMP -> $REPORT_URL (Thread=$THREADS, Loop=$LOOPS_PER_THREAD)" >> "$INDEX_FILE" if [ $EXIT_CODE -ne 0 ]; then echo "⚠️ Test con $THREADS thread fallito!" break else MAX_SUCCESS_THREADS=$THREADS echo "✅ Test con $THREADS thread completato correttamente." fi # Piccola pausa tra i test sleep 5 done echo " Stress test progressivo completato!" echo "Numero massimo di thread gestibili senza errori: $MAX_SUCCESS_THREADS" echo "Indice dei report aggiornato in: $INDEX_FILE" echo "Indice completo: http://localhost/reports/report_index.txt" #################################### |
Assegnare i permessi di esecuzione dello script quindi eseguirlo con i comandi:
|
0 1 |
chmod +x run_progressive_stress.sh ./run_progressive_stress.sh test.jmx |
Andando sul link indicato si accede all’indice dei test effettuati da cui è possibile visualizzare il risultato
CONSIDERAZIONI FINALI
⚙️ Pro di Apache JMeter
✅ 1. Open Source e gratuito
Completamente gratuito e supportato da una grande community.
Costantemente aggiornato con plugin e integrazioni.
✅ 2. Facile da configurare e utilizzare
Interfaccia grafica intuitiva per creare test plan senza dover scrivere codice complesso.
Supporta anche modalità headless per esecuzioni automatizzate.
✅ 3. Supporto multi-protocollo
Non solo HTTP/S: supporta anche FTP, JDBC, SOAP, REST, JMS, LDAP, TCP, e altri protocolli.
Può essere usato per testare API, web app, servizi backend e persino sistemi legacy.
✅ 4. Alta scalabilità
Permette di distribuire i test su più macchine (Distributed Testing), simulando migliaia di utenti contemporanei.
✅ 5. Ampie possibilità di reportistica
Genera grafici, report HTML e metriche dettagliate (tempo di risposta, throughput, errori, ecc.).
Può essere integrato con Grafana, InfluxDB, Prometheus e altre dashboard per il monitoraggio in tempo reale.
✅ 6. Automatizzabile
Può essere integrato facilmente in pipeline CI/CD (Jenkins, GitLab CI, Azure DevOps, ecc.).
Supporta l’esecuzione tramite script e comandi CLI.
✅ 7. Grande comunità e risorse
Ampia documentazione, tutorial, plugin e forum attivi.
Molti template e best practice disponibili online.
⚠️ Contro di Apache JMeter
❌ 1. Uso intensivo di risorse
L’interfaccia GUI e i test di grandi dimensioni possono consumare molta memoria e CPU.
Spesso è necessario distribuire i test su più nodi per mantenere prestazioni accettabili.
❌ 2. Interfaccia datata
L’interfaccia utente è funzionale ma poco moderna e talvolta poco intuitiva per utenti alle prime armi.
❌ 3. Limitazioni nei test reali su browser
Non simula il comportamento di un browser reale (niente rendering, JavaScript, cache, ecc.).
Per test end-to-end reali è meglio affiancarlo ad altri strumenti (es. Selenium o Playwright).
❌ 4. Gestione complessa per scenari avanzati
Test complessi o dinamici (autenticazioni, token, variabili di sessione) richiedono conoscenze tecniche approfondite.
❌ 5. Analisi dati non sempre immediata
I report integrati sono utili ma limitati; per analisi approfondite serve esportare i dati in strumenti esterni.
❌ 6. Scalabilità manuale
La configurazione del Distributed Mode può risultare macchinosa, specialmente in ambienti cloud o containerizzati.



0 commenti