Solución de Profiling para ProCash
JMeter + VisualVM
Problema
El Desafío
- Aplicación legacy: Java 1.7 + Tomcat 7
- 9 aplicaciones web en producción
- Problemas de rendimiento sin identificar
- Herramientas comerciales incompatibles (YourKit, JProfiler)
Lo Que Necesitamos
- Identificar cuellos de botella (CPU, memoria)
- Detectar memory leaks
- Analizar comportamiento bajo carga real
- Sin modificar código de producción
Solución Propuesta
Herramientas Open Source
- JMeter 3.1 - Generación de carga realista
- VisualVM 1.3.9 - Profiling y análisis
¿Por qué estas herramientas?
- Gratuitas (open source)
- Compatible con Java 1.7
- Sin instalación en producción (solo JVM args)
- Estándares de industria
- Bajo overhead (<5%)
Arquitectura de la Solución
┌─────────────────────────────────────────────────┐
│ MÁQUINA DEL OPERADOR (Local PC) │
│ │
│ ┌────────────────┐ ┌──────────────────┐ │
│ │ JMeter 3.1 │ │ VisualVM 1.3.9 │ │
│ │ Genera carga │ │ Monitorea app │ │
│ │ HTTP/JSON-RPC │ │ CPU/Memoria/GC │ │
│ └────────┬───────┘ └────────┬─────────┘ │
└───────────┼──────────────────────┼──────────────┘
│ │
│ HTTP (puerto 8080) │ JMX (puerto 9999)
│ │
┌───────────▼──────────────────────▼──────────────┐
│ EC2 DE PRUEBAS (Test Environment) │
│ │
│ ┌────────────────────────────────────────────┐ │
│ │ Tomcat 7.0.99 + 9 Apps ProCash │ │
│ │ Java 1.7 con JMX habilitado │ │
│ │ GC logging activo │ │
│ └────────────────────────────────────────────┘ │
│ │
│ Resultados guardados localmente: │
│ - /opt/tomcat/logs/gc.log │
│ - Heap dumps (bajo demanda) │
│ - Thread dumps (bajo demanda) │
└─────────────────────────────────────────────────┘
Componente 1: JMeter
¿Qué hace?
- Graba flujos de usuario reales (login, navegación, formularios)
- Reproduce esos flujos con N usuarios concurrentes
- Mide tiempos de respuesta, throughput, errores
Cómo funciona
- Actúa como proxy HTTP (puerto 8888)
- El navegador envía tráfico a través del proxy
- JMeter graba todas las peticiones HTTP/JSON-RPC
- Guarda el flujo como "test plan" (.jmx)
- Reproduce el flujo con 1, 5, 10, 20+ usuarios
Resultado
- Carga realista (no sintética)
- Incluye sesiones, cookies, tokens CSRF
- Reportes HTML con gráficas de rendimiento
Componente 2: VisualVM
¿Qué hace?
- Monitorea la JVM en tiempo real (vía JMX remoto)
- Identifica métodos que consumen CPU (hotspots)
- Detecta memory leaks y objetos retenidos
- Analiza threads (deadlocks, contención)
- Visualiza comportamiento del GC
Cómo funciona
- Se conecta al puerto JMX 9999 del servidor
- No requiere instalar agente (usa JMX estándar de Java)
- Observa la app mientras JMeter genera carga
- Toma snapshots para análisis offline
Resultado
- Perfil de CPU (qué métodos son lentos)
- Heap dumps (análisis de memoria)
- Thread dumps (estado de hilos)
Integración: JMeter + VisualVM
Trabajan en Paralelo
Terminal 1: Terminal 2:
$ visualvm $ jmeter -n -t test.jmx
(conectado a JMX 9999) (genera carga HTTP)
│ │
└────────► Observa ◄────────────┘
│
┌─────▼─────┐
│ Tomcat │
│ Apps │
└───────────┘
Flujo de Trabajo
- Iniciar VisualVM → Conectar a puerto 9999
- Activar profiling de CPU en VisualVM
- Ejecutar JMeter → Carga progresiva (1→5→10→20 usuarios)
- Observar en VisualVM → Identificar hotspots
- Guardar snapshots → Análisis posterior
¿Qué se Instala en Producción/Pruebas?
Respuesta: NADA adicional
Solo se necesitan argumentos JVM en setenv.sh:
# JMX Remote (para VisualVM)
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port=9999
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false
# GC Logging (para análisis de memoria)
-verbose:gc
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-Xloggc:/opt/tomcat/logs/gc.log
Esto ya está configurado
Ubicación de Resultados
Opción 1: Resultados en Máquina Local (Recomendado)
~/profiling-results/
├── jmeter-testplans/
│ └── procash-workflow.jmx # Test plan grabado
│
├── jmeter-results/
│ ├── results-20251006.jtl # Datos raw de JMeter
│ └── html-report/ # Dashboard HTML
│ └── index.html # ← Abrir en navegador
│
├── visualvm-snapshots/
│ ├── cpu-profile-20251006.nps # Perfil de CPU
│ ├── heap-dump-20251006.hprof # Análisis de memoria
│ └── thread-dump-20251006.txt # Estado de threads
│
└── gc-logs/
└── gc-20251006.log # Log de GC (copiado de servidor)
Ventajas:
- Todos los datos en tu PC
- Análisis offline (sin conexión al servidor)
- Fácil de compartir (carpeta comprimida)
- No consume espacio en servidor
Ubicación de Resultados
Opción 2: Resultados en Servidor EC2
# En el servidor EC2 de pruebas
/opt/profiling-results/
├── jmeter-reports/
│ └── 20251006/index.html
├── heap-dumps/
│ └── heap-20251006.hprof
└── gc-logs/
└── gc.log
Ventajas:
- Centralizado (un solo lugar)
- Automatizable (cron jobs)
Desventaja: Consume espacio en disco
Descarga de Resultados y Dashboards
Descargar Resultados del Servidor
# Opción 1: SCP (copiar archivos individuales)
scp ec2-user@server:/opt/tomcat/logs/gc.log ~/profiling-results/
# Opción 2: Rsync (sincronizar carpetas completas)
rsync -avz ec2-user@server:/opt/profiling-results/ ~/profiling-results/
# Opción 3: Comprimir y descargar
ssh ec2-user@server "tar czf /tmp/results.tar.gz /opt/profiling-results"
scp ec2-user@server:/tmp/results.tar.gz ~/
tar xzf results.tar.gz
Visualizar Localmente
# Reporte JMeter (dashboard HTML)
firefox ~/profiling-results/jmeter-results/html-report/index.html
# VisualVM snapshots
visualvm --openfile ~/profiling-results/visualvm-snapshots/cpu-profile.nps
# Heap dumps
visualvm --openfile ~/profiling-results/heap-dump.hprof
Arquitectura Completa: Test + Producción
┌──────────────────────────────────────────────────────────┐
│ MÁQUINA LOCAL (Operador) │
│ - JMeter: genera carga │
│ - VisualVM: analiza resultados │
│ - Todos los resultados guardados localmente │
└────────┬──────────────────────────┬──────────────────────┘
│ │
│ │
┌────────▼──────────────┐ ┌────────▼──────────────────────┐
│ EC2 TEST │ │ EC2 PRODUCCIÓN │
│ - Profiling activo │ │ - Solo monitoreo básico │
│ - JMeter load tests │ │ - JMX pasivo │
│ - VisualVM profiling │ │ - GC logging │
│ - Heap dumps │ │ - Sin profiling CPU │
└───────────────────────┘ └───────────────────────────────┘
│ │
│ │
└──────► Compara ◄─────────┘
Resultados
(Test vs Producción)
Flujo de Datos: De la Grabación al Dashboard
Paso 1: Grabación (Una sola vez)
Navegador ──proxy──► JMeter Recorder ──► test-plan.jmx
Paso 2: Ejecución en Test
JMeter (local) ──HTTP──► Tomcat (test EC2)
VisualVM (local) ──JMX──► Tomcat (test EC2)
Paso 3: Recolección de Resultados
Test EC2:
/opt/tomcat/logs/gc.log ──┐
├──► Descargar vía SCP
JMeter (local): │
results.jtl ──────────────┤
│
VisualVM (local): │
cpu-profile.nps ──────────┘
Paso 4: Análisis y Dashboard
~/profiling-results/ ──► JMeter HTML Report
──► VisualVM Analysis
──► Excel Charts
──► Jupyter Notebook
──► Grafana (opcional)
Ejemplo de Resultados: Dashboard JMeter
Métricas Incluidas
- Response Time Over Time (gráfica de tiempo de respuesta)
- Throughput (peticiones por segundo)
- Active Threads (usuarios concurrentes)
- Response Time Percentiles (p50, p95, p99)
- Error Rate (% de errores)
- Top 5 Slowest Requests (peticiones más lentas)
Ejemplo de Resultados: VisualVM
CPU Profiling
Método Tiempo Total Self Time
============================================================
ServletLogin.doPost() 45.2% 12.3%
├─ DatabaseService.query() 32.9% 28.1%
│ └─ ResultSet.next() 4.8% 4.8%
└─ SessionManager.create() 0.0% 0.0%
Conclusión: El query de login consume 28% del CPU total
Memory Analysis (Heap Dump)
Clase Instancias Tamaño (MB)
====================================================
char[] 1,245,892 487.2
String 892,103 142.7
SessionData 12,500 89.3 ← Posible leak
byte[] 456,789 78.9
Conclusión: 12,500 sesiones acumuladas (memory leak)
Comparación: Solución Propuesta vs Alternativas
| Característica | JMeter + VisualVM | YourKit | JProfiler | AWS CodeGuru |
|---|---|---|---|---|
| Costo | $0 | licencia | licencia | $43/mes |
| Java 1.7 | ✅ Sí | ❌ No | ❌ No | ✅ Sí |
| Instalación | Solo JVM args | Agente | Agente | Agente |
| Overhead | <5% | 10-20% | 10-20% | 5-10% |
| Offline | ✅ Sí | ✅ Sí | ✅ Sí | ❌ No |
| Carga real | ✅ JMeter | ❌ No | ❌ No | ✅ Sí |
Ganador: JMeter + VisualVM por costo cero y compatibilidad total
Proceso de Implementación
Fase 1: Configuración (Una vez)
- Configurar JVM args en test EC2 (ya hecho)
- Instalar JMeter en máquina local (script disponible)
- Instalar VisualVM en máquina local
- Crear túnel SSH o VPN a test EC2
Fase 2: Grabación de Workflows (Una vez por flujo)
- Iniciar JMeter recorder
- Configurar proxy en navegador
- Ejecutar flujo de usuario real
- Guardar test plan (.jmx)
Fase 3: Profiling (Repetible)
- Conectar VisualVM a test EC2
- Ejecutar JMeter load test
- Observar hotspots en VisualVM
- Guardar snapshots
- Descargar logs del servidor
Automatización
Script de Profiling Automático
#!/bin/bash
# profiling-automatico.sh
FECHA=$(date +%Y%m%d_%H%M%S)
RESULTADOS=~/profiling-results/$FECHA
# 1. Ejecutar JMeter
jmeter -n -t workflow.jmx \
-Jusers=20 \
-l $RESULTADOS/results.jtl \
-e -o $RESULTADOS/html-report/
# 2. Descargar logs del servidor
scp ec2-user@test-server:/opt/tomcat/logs/gc.log $RESULTADOS/
# 3. Tomar heap dump (vía JMX)
jmap -dump:live,format=b,file=$RESULTADOS/heap.hprof <PID>
# 4. Generar reporte
echo "Profiling completado: $RESULTADOS"
firefox $RESULTADOS/html-report/index.html
Programar con Cron
Entregables
1. Reportes Técnicos
- Reporte HTML de JMeter (métricas de carga)
- Snapshots de VisualVM (CPU, memoria, threads)
- Análisis de GC logs (comportamiento del garbage collector)
2. Documentación
- Test plans (.jmx) - reproducibles
- Guías de ejecución (paso a paso)
- Scripts de automatización (bash)
3. Presentaciones
- Dashboard ejecutivo (resumen de hallazgos)
- Recomendaciones técnicas (optimizaciones prioritarias)
- Plan de acción (qué optimizar primero)
Ejemplo: Hallazgos Típicos
CPU Hotspots
❌ Problema: ServletLogin.doPost() consume 45% del CPU
✅ Solución: Agregar índice en tabla usuarios (columna email)
📊 Impacto: Reduce CPU a 12%, mejora respuesta en 300ms
Memory Leaks
❌ Problema: 12,500 objetos SessionData retenidos (89 MB)
✅ Solución: Implementar timeout de sesión (30 minutos)
📊 Impacto: Reduce uso de heap en 65%
GC Thrashing
❌ Problema: GC cada 5 segundos, pausas de 200ms
✅ Solución: Aumentar heap de 1GB a 2GB, ajustar CMSInitiatingOccupancyFraction
📊 Impacto: GC cada 20 segundos, pausas de 50ms
Beneficios de esta Solución
Técnicos
- Sin cambios de código (no invasivo)
- Bajo overhead (seguro en test)
- Resultados accionables (identificación precisa)
- Reproducible (test plans guardados)
Operacionales
- Costo cero (no licencias)
- Control total (open source, datos propios)
- Escalable (múltiples servidores sin costo adicional)
- Transferencia de conocimiento (herramientas estándar)
Preguntas Frecuentes
¿Cuánto tiempo toma una sesión de profiling?
- Grabación de workflow: 15-30 minutos
- Ejecución de test: 10-30 minutos (según duración)
- Análisis de resultados: 1-2 horas
- Total: Medio día por sesión completa
¿Los resultados se quedan en el servidor?
No (recomendado):
- JMeter guarda resultados en tu PC local (
.jtl, HTML report) - VisualVM guarda snapshots en tu PC local (
.nps,.hprof) - GC logs se descargan del servidor después del test
- Ventaja: Análisis offline, no consume espacio en servidor
¿Se puede crear un dashboard?
Sí, varias opciones:
- Simple: Reporte HTML de JMeter (ya incluido)
- Medio: Excel/Google Sheets (importar CSV)
- Avanzado: Grafana + InfluxDB (tiempo real)
- Custom: Jupyter Notebook + Python (análisis personalizado)
¿Qué pasa si encontramos un problema?
Flujo de resolución:
-
VisualVM identifica el método problemático (e.g.,
getUserData()) - Reporte incluye stack trace y tiempo de CPU
- Desarrollador revisa código del método
- Se implementa optimización
- Se re-profila para validar mejora
¿Cuántos usuarios podemos simular?
Depende de:
- Hardware de la máquina con JMeter
- Complejidad del workflow
- Típicamente: 50-100 usuarios concurrentes sin problema
- Con múltiples máquinas JMeter: 1000+ usuarios
Referencias
- JMeter: https://jmeter.apache.org/
- VisualVM: https://visualvm.github.io/
- Documentación JMX: https://docs.oracle.com/javase/7/docs/technotes/guides/management/
Solución de Profiling para ProCash
By Juan G
Solución de Profiling para ProCash
- 34