kukuy 1.5.0 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/README.md +159 -188
  2. package/balancer.log +30 -0
  3. package/certs/auto/certificate.crt +22 -0
  4. package/certs/auto/private.key +28 -0
  5. package/kukuy-plugins/README.md +125 -0
  6. package/kukuy-plugins/cache-plugin/index.js +477 -0
  7. package/kukuy-plugins/cache-plugin/manifest.json +17 -0
  8. package/kukuy-plugins/ejemplo-plugin/index.js +41 -0
  9. package/kukuy-plugins/ejemplo-plugin/manifest.json +11 -0
  10. package/kukuy-plugins/health-checker/index.js +168 -0
  11. package/kukuy-plugins/health-checker/manifest.json +16 -0
  12. package/kukuy-plugins/health-monitor/index.js +58 -0
  13. package/kukuy-plugins/health-monitor/manifest.json +16 -0
  14. package/kukuy-plugins/redirect-plugin/index.js +172 -0
  15. package/kukuy-plugins/redirect-plugin/manifest.json +15 -0
  16. package/package.json +7 -3
  17. package/servers_real.json +5 -0
  18. package/src/core/Balancer.js +176 -39
  19. package/src/core/ServerPool.js +2 -2
  20. package/src/extensibility/ExtendedFilterChain.js +90 -0
  21. package/src/extensibility/ExtendedHookManager.js +87 -0
  22. package/src/extensibility/FilterChain.js +2 -9
  23. package/src/extensibility/HookManager.js +1 -0
  24. package/src/extensibility/PostStartupExtension.js +97 -0
  25. package/src/plugins/PluginManager.js +231 -0
  26. package/src/utils/HealthChecker.js +61 -6
  27. package/.ctagsd/ctagsd.json +0 -954
  28. package/.ctagsd/file_list.txt +0 -100
  29. package/.ctagsd/tags.db +0 -0
  30. package/CHANGELOG.md +0 -125
  31. package/LICENSE +0 -680
  32. package/README-SSL.md +0 -165
  33. package/captura.png +0 -0
  34. package/kukuu1.webp +0 -0
  35. package/kukuy.workspace +0 -11
  36. package/optimize-mariadb.sh +0 -152
  37. package/restart-balancer.sh +0 -10
  38. package/scripts/load_test.py +0 -151
  39. package/stress-test.js +0 -190
  40. package/test_optimization.js +0 -54
package/README-SSL.md DELETED
@@ -1,165 +0,0 @@
1
- # Kukuy - Balanceador de Carga con SSL
2
-
3
- ![Kukuy Logo](kukuu1.webp)
4
-
5
- Este documento explica cómo configurar e iniciar el balanceador Kukuy con soporte SSL/TLS.
6
-
7
- ## Información del Proyecto
8
-
9
- - **Página Oficial**: [https://bsanchez.unaux.com/](https://bsanchez.unaux.com/)
10
- - **Repositorio Oficial**: [https://gitlab.com/bytedogssyndicate1/kukuy](https://gitlab.com/bytedogssyndicate1/kukuy)
11
-
12
- ## Contenido
13
-
14
- - [Requisitos](#requisitos)
15
- - [Configuración de SSL](#configuración-de-ssl)
16
- - [Inicio del Balanceador con SSL](#inicio-del-balanceador-con-ssl)
17
- - [Uso de ngrok con SSL](#uso-de-ngrok-con-ssl)
18
- - [Scripts Disponibles](#scripts-disponibles)
19
- - [Variables de Entorno](#variables-de-entorno)
20
- - [Configuración de Servidores](#configuración-de-servidores)
21
-
22
- ## Requisitos
23
-
24
- - Node.js instalado
25
- - OpenSSL (para generar certificados autofirmados)
26
- - Opcional: ngrok para exponer servicios locales a Internet
27
-
28
- ## Configuración de SSL
29
-
30
- ### Generar Certificados Autofirmados
31
-
32
- Para generar certificados SSL autofirmados, ejecuta el siguiente comando:
33
-
34
- ```bash
35
- mkdir -p certs/auto
36
- openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout certs/auto/private.key -out certs/auto/certificate.crt -subj "/C=ES/ST=Madrid/L=Madrid/O=Kukuy/OU=IT Department/CN=localhost"
37
- ```
38
-
39
- Esto creará los siguientes archivos:
40
- - `certs/auto/certificate.crt`: El certificado SSL
41
- - `certs/auto/private.key`: La llave privada
42
-
43
- ### Usar Certificados Existentes
44
-
45
- Si tienes certificados SSL válidos emitidos por una CA, colócalos en el directorio `certs/auto` o en cualquier ubicación de tu elección y actualiza las rutas en las variables de entorno.
46
-
47
- ## Inicio del Balanceador con SSL
48
-
49
- ### Método 1: Variables de Entorno Directas
50
-
51
- ```bash
52
- SSL_CERT_PATH=certs/auto/certificate.crt SSL_KEY_PATH=certs/auto/private.key BALANCER_HTTPS_PORT=8443 CONFIG_FILE_PATH=./servers_real.json node kukuy.js
53
- ```
54
-
55
- ### Método 2: Usando Scripts Preparados
56
-
57
- El proyecto incluye scripts para facilitar el inicio:
58
-
59
- ```bash
60
- # Iniciar con SSL usando el script básico
61
- ./start-ssl.sh
62
-
63
- # Iniciar con SSL usando el script de configuración
64
- ./start-ssl-config.sh
65
- ```
66
-
67
- Por defecto, ambos scripts utilizan el archivo `servers_real.json` para la configuración de servidores.
68
-
69
- ## Uso de ngrok con SSL
70
-
71
- Para exponer tu balanceador SSL a Internet a través de ngrok:
72
-
73
- 1. Instala ngrok desde https://ngrok.com/download
74
- 2. Ejecuta el siguiente comando:
75
-
76
- ```bash
77
- ngrok tls 8443
78
- ```
79
-
80
- Esto creará un túnel seguro que redirigirá las conexiones SSL a tu balanceador local en el puerto 8443.
81
-
82
- Alternativamente, puedes usar un túnel HTTP si prefieres que ngrok maneje la terminación SSL:
83
-
84
- ```bash
85
- ngrok http https://localhost:8443
86
- ```
87
-
88
- ## Scripts Disponibles
89
-
90
- ### `start-ssl.sh`
91
-
92
- Script básico para iniciar Kukuy en modo SSL con valores predeterminados:
93
- - Puerto HTTPS: 8443
94
- - Archivo de certificado: `certs/auto/certificate.crt`
95
- - Archivo de llave privada: `certs/auto/private.key`
96
- - Archivo de configuración de servidores: `servers_real.json`
97
-
98
- Opcionalmente, puedes habilitar el modo HTTP simultáneamente:
99
- ```bash
100
- ENABLE_HTTP=true ./start-ssl.sh
101
- ```
102
-
103
- ### `start-ssl-config.sh`
104
-
105
- Script que carga la configuración desde el archivo `.env.ssl`. Este archivo contiene todas las variables de entorno necesarias para la operación del balanceador con SSL.
106
-
107
- ## Variables de Entorno
108
-
109
- Las siguientes variables de entorno controlan la configuración SSL:
110
-
111
- - `SSL_CERT_PATH`: Ruta al archivo de certificado SSL
112
- - `SSL_KEY_PATH`: Ruta al archivo de llave privada SSL
113
- - `BALANCER_HTTPS_PORT`: Puerto en el que escuchará el servidor HTTPS
114
- - `BALANCER_HTTP_PORT`: Puerto opcional para el servidor HTTP (si se desea mantener ambos)
115
- - `CONFIG_FILE_PATH`: Ruta al archivo de configuración de servidores
116
- - `ROUTES_FILE_PATH`: Ruta al archivo de configuración de rutas
117
- - `LOG_LEVEL`: Nivel de logging (info, debug, warn, error)
118
- - `HEALTH_CHECK_INTERVAL`: Intervalo para verificaciones de salud de servidores (en milisegundos)
119
-
120
- ## Configuración de Servidores
121
-
122
- El balanceador puede leer la configuración de servidores desde archivos JSON. Por defecto, este setup utiliza `servers_real.json` que contiene:
123
-
124
- ```json
125
- {
126
- "servers": [
127
- {
128
- "url": "http://localhost:3434",
129
- "weight": 1,
130
- "tags": ["backend"]
131
- },
132
- {
133
- "url": "http://localhost:8765",
134
- "weight": 1,
135
- "tags": ["backend"]
136
- },
137
- {
138
- "url": "http://localhost:5445",
139
- "weight": 1,
140
- "tags": ["backend"]
141
- }
142
- ]
143
- }
144
- ```
145
-
146
- Puedes modificar este archivo para apuntar a tus servidores backend reales o crear nuevos archivos de configuración según sea necesario.
147
-
148
- ## Panel de Control
149
-
150
- Una vez iniciado el balanceador, el panel de control web estará disponible en:
151
- - HTTP: http://localhost:8082
152
-
153
- ## Solución de Problemas
154
-
155
- ### Certificados SSL Autofirmados
156
-
157
- Cuando uses certificados autofirmados, los navegadores mostrarán advertencias de seguridad. Esto es normal y esperado. Para producción, utiliza certificados firmados por una Autoridad de Certificación (CA) reconocida.
158
-
159
- ### Puertos Bloqueados
160
-
161
- Asegúrate de que los puertos que deseas usar (por defecto 8443 para HTTPS y 8082 para el panel) estén disponibles y no sean usados por otros servicios.
162
-
163
- ### Conexión a Servidores Backend
164
-
165
- Verifica que los servidores backend definidos en el archivo de configuración estén accesibles desde la máquina donde corre el balanceador.
package/captura.png DELETED
Binary file
package/kukuu1.webp DELETED
Binary file
package/kukuy.workspace DELETED
@@ -1,11 +0,0 @@
1
- {
2
- "NodeJS": {
3
- "metadata": {
4
- "version": "1.0",
5
- "ide": "CodeLite",
6
- "type": "NodeJS"
7
- },
8
- "folders": ["."],
9
- "m_showHiddenFiles": false
10
- }
11
- }
@@ -1,152 +0,0 @@
1
- #!/bin/bash
2
-
3
- # Script para optimizar MariaDB y liberar espacio en disco
4
- # Autor: Sistema de Optimización
5
- # Fecha: $(date +%Y-%m-%d)
6
-
7
- set -e # Salir si ocurre algún error
8
-
9
- echo "=== Iniciando optimización de MariaDB y liberación de espacio ==="
10
-
11
- # Crear directorio para backups
12
- BACKUP_DIR="/home/bds/kukuy/backups-$(date +%Y%m%d_%H%M%S)"
13
- mkdir -p "$BACKUP_DIR"
14
-
15
- echo "Directorio de backup: $BACKUP_DIR"
16
-
17
- # 1. BACKUP DE ARCHIVOS DE CONFIGURACIÓN DE MARIADB
18
- echo ""
19
- echo "1. Realizando backup de archivos de configuración..."
20
-
21
- CONFIG_FILES=(
22
- "/etc/mysql/mariadb.conf.d/50-server.cnf"
23
- "/etc/mysql/mariadb.conf.d/50-client.cnf"
24
- "/etc/mysql/mariadb.conf.d/50-mysql-clients.cnf"
25
- "/etc/mysql/my.cnf"
26
- "/etc/mysql/debian-start"
27
- )
28
-
29
- for config_file in "${CONFIG_FILES[@]}"; do
30
- if [ -f "$config_file" ]; then
31
- cp "$config_file" "$BACKUP_DIR/$(basename "$config_file")"
32
- echo " - Backup de $config_file realizado"
33
- else
34
- echo " - $config_file no encontrado"
35
- fi
36
- done
37
-
38
- # 2. LIBERAR ESPACIO EN DISCO
39
- echo ""
40
- echo "2. Liberando espacio en disco..."
41
-
42
- # Limpiar paquetes desactualizados
43
- echo " - Limpiando paquetes desactualizados..."
44
- sudo apt autoremove -y 2>/dev/null || echo " - No se pudo ejecutar apt autoremove (sin privilegios)"
45
-
46
- # Limpiar cache de paquetes
47
- echo " - Limpiando cache de paquetes..."
48
- sudo apt autoclean 2>/dev/null || echo " - No se pudo ejecutar apt autoclean (sin privilegios)"
49
-
50
- # Limpiar logs antiguos (sin sudo)
51
- echo " - Buscando logs grandes en /tmp..."
52
- find /tmp -name "*.log" -size +100M -delete 2>/dev/null || echo " - No se pudieron eliminar logs en /tmp (sin privilegios)"
53
-
54
- # Limpiar historial de comandos si es necesario
55
- echo " - Verificando tamaño de archivos de historial..."
56
- if [ -f "$HOME/.bash_history" ] && [ $(stat -c%s "$HOME/.bash_history" 2>/dev/null || echo 0) -gt 100000 ]; then
57
- tail -n 1000 "$HOME/.bash_history" > "$HOME/.bash_history.tmp" && mv "$HOME/.bash_history.tmp" "$HOME/.bash_history"
58
- echo " Archivo de historial reducido"
59
- fi
60
-
61
- # 3. OPTIMIZACIÓN DE CONFIGURACIÓN DE MARIADB
62
- echo ""
63
- echo "3. Optimizando configuración de MariaDB..."
64
-
65
- SERVER_CONFIG="/etc/mysql/mariadb.conf.d/50-server.cnf"
66
-
67
- if [ -f "$SERVER_CONFIG" ]; then
68
- # Crear copia de seguridad temporal
69
- TEMP_CONFIG="$BACKUP_DIR/50-server.cnf.optimized"
70
- cp "$SERVER_CONFIG" "$TEMP_CONFIG"
71
-
72
- # Aplicar optimizaciones (solo si se puede escribir)
73
- if [ -w "$SERVER_CONFIG" ]; then
74
- echo " - Aplicando optimizaciones a $SERVER_CONFIG"
75
-
76
- # Ajustar tamaño del buffer pool de InnoDB (si hay suficiente RAM)
77
- TOTAL_RAM=$(free -m | awk 'NR==2{print $2}')
78
- if [ $TOTAL_RAM -gt 2048 ]; then
79
- # Si hay más de 2GB de RAM, asignar 25% a buffer pool
80
- BUFFER_POOL_SIZE=$((TOTAL_RAM * 25 / 100))
81
- BUFFER_POOL_BYTES=$((BUFFER_POOL_SIZE * 1024 * 1024))
82
- else
83
- # Si hay menos de 2GB, asignar 128MB
84
- BUFFER_POOL_BYTES=134217728
85
- fi
86
-
87
- # Ajustar configuración de logs para reducir uso de disco
88
- sudo tee -a "$SERVER_CONFIG" > /dev/null << EOF
89
-
90
- # Optimizaciones añadidas por optimize-mariadb.sh
91
- [mysqld]
92
- # Ajustar tamaño del buffer pool de InnoDB
93
- innodb_buffer_pool_size = ${BUFFER_POOL_BYTES}
94
-
95
- # Reducir tamaño de logs para ahorrar espacio en disco
96
- innodb_log_file_size = 67108864 # 64MB
97
- innodb_log_files_in_group = 2
98
-
99
- # Configurar rotación de logs binarios
100
- expire_logs_days = 3
101
- max_binlog_size = 100M
102
-
103
- # Limitar tamaño de queries temporales
104
- tmp_table_size = 64M
105
- max_heap_table_size = 64M
106
-
107
- # Ajustar timeouts para liberar conexiones rápidamente
108
- wait_timeout = 300
109
- interactive_timeout = 300
110
-
111
- # Habilitar slow query log con umbral razonable
112
- slow_query_log = 1
113
- long_query_time = 2
114
- slow_query_log_file = /var/log/mysql/slow.log
115
- EOF
116
- echo " Configuración optimizada aplicada"
117
- else
118
- echo " No se tienen permisos para modificar $SERVER_CONFIG"
119
- echo " Las optimizaciones recomendadas son:"
120
- echo " - innodb_buffer_pool_size = [25% de RAM disponible]"
121
- echo " - innodb_log_file_size = 64M"
122
- echo " - expire_logs_days = 3"
123
- echo " - wait_timeout = 300"
124
- fi
125
- else
126
- echo " - $SERVER_CONFIG no encontrado"
127
- fi
128
-
129
- # 4. ANALIZAR USO DE ESPACIO EN MARIADB
130
- echo ""
131
- echo "4. Analizando uso de espacio en bases de datos..."
132
-
133
- # Obtener información de tamaño de bases de datos
134
- mysql -u root -e "
135
- SELECT
136
- table_schema AS 'Base de Datos',
137
- ROUND(SUM(data_length + index_length) / 1024 / 1024, 2) AS 'Tamaño (MB)'
138
- FROM information_schema.tables
139
- GROUP BY table_schema
140
- ORDER BY (data_length + index_length) DESC;
141
- " 2>/dev/null || echo " - No se pudo acceder a la información de bases de datos"
142
-
143
- # 5. RECOMENDACIONES FINALES
144
- echo ""
145
- echo "=== Recomendaciones ==="
146
- echo "1. Revisa el directorio de backup: $BACKUP_DIR"
147
- echo "2. Reinicia MariaDB para aplicar cambios de configuración:"
148
- echo " sudo systemctl restart mariadb"
149
- echo "3. Considera eliminar archivos innecesarios en el sistema"
150
- echo "4. Monitorea el uso de disco regularmente con 'df -h'"
151
- echo ""
152
- echo "=== Optimización completada ==="
@@ -1,10 +0,0 @@
1
- #!/bin/bash
2
- # Script para matar e iniciar KUKUY con estadísticas por servidor y WebSocket
3
-
4
- echo "Matando procesos existentes de KUKUY..."
5
- pkill -f "node kukuy.js" 2>/dev/null || true
6
-
7
- sleep 2
8
-
9
- echo "Iniciando KUKUY con estadísticas por servidor y WebSocket..."
10
- LOAD_BALANCING_ALGORITHM=roundrobin CONFIG_FILE_PATH=./servers_real.json BALANCER_HTTP_PORT=8088 DASHBOARD_PORT=8082 WEBSOCKET_PORT=8084 node kukuy.js
@@ -1,151 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- Script de prueba de carga concurrente para el balanceador
4
- """
5
-
6
- import threading
7
- import time
8
- import requests
9
- import sys
10
- from datetime import datetime
11
-
12
- # Configuración
13
- BASE_URL = "http://localhost:8081"
14
- ENDPOINT = "/api/endpoints"
15
- NUM_THREADS = 100 # Número de hilos concurrentes para alta carga
16
- REQUESTS_PER_THREAD = 6000 # Solicitudes por hilo (100 hilos * 60 solicitudes = 6000 solicitudes totales por segundo)
17
- DURATION_SECONDS = 120 # Duración de la prueba en segundos (2 minutos)
18
- TARGET_RPS = 5000 # Objetivo de solicitudes por segundo
19
-
20
- # Variables globales para métricas
21
- total_requests = 0
22
- successful_requests = 0
23
- failed_requests = 0
24
- lock = threading.Lock()
25
-
26
- def make_requests_continuous(thread_id, end_time_target):
27
- """Función que realiza solicitudes HTTP continuas hasta alcanzar la duración objetivo"""
28
- global total_requests, successful_requests, failed_requests
29
-
30
- thread_start_time = time.time()
31
- thread_successful = 0
32
- thread_failed = 0
33
-
34
- # Calcular el intervalo entre solicitudes para alcanzar el objetivo RPS
35
- interval = 1.0 / (TARGET_RPS / NUM_THREADS) if NUM_THREADS > 0 else 0.001 # Evitar división por cero
36
-
37
- while time.time() < end_time_target:
38
- try:
39
- response = requests.get(f"{BASE_URL}{ENDPOINT}", timeout=10)
40
- with lock:
41
- total_requests += 1
42
- if response.status_code == 200:
43
- successful_requests += 1
44
- thread_successful += 1
45
- else:
46
- failed_requests += 1
47
- thread_failed += 1
48
- except requests.exceptions.RequestException as e:
49
- with lock:
50
- total_requests += 1
51
- failed_requests += 1
52
- thread_failed += 1
53
- if thread_id == 0: # Solo imprimir errores para el primer hilo
54
- print(f"[Thread-{thread_id}] Error en solicitud: {e}")
55
-
56
- # Esperar el intervalo calculado para alcanzar el objetivo RPS
57
- time.sleep(interval)
58
-
59
- thread_end_time = time.time()
60
- thread_duration = thread_end_time - thread_start_time
61
-
62
- print(f"[Thread-{thread_id}] Completado: {thread_successful} exitosas, {thread_failed} fallidas en {thread_duration:.2f}s")
63
-
64
-
65
- def make_requests(thread_id):
66
- """Función original que realiza solicitudes HTTP en un hilo (mantenida por compatibilidad)"""
67
- global total_requests, successful_requests, failed_requests
68
-
69
- thread_start_time = time.time()
70
- thread_successful = 0
71
- thread_failed = 0
72
-
73
- for i in range(REQUESTS_PER_THREAD):
74
- try:
75
- response = requests.get(f"{BASE_URL}{ENDPOINT}", timeout=10)
76
- with lock:
77
- total_requests += 1
78
- if response.status_code == 200:
79
- successful_requests += 1
80
- thread_successful += 1
81
- else:
82
- failed_requests += 1
83
- thread_failed += 1
84
- except requests.exceptions.RequestException as e:
85
- with lock:
86
- total_requests += 1
87
- failed_requests += 1
88
- thread_failed += 1
89
- if thread_id == 0: # Solo imprimir errores para el primer hilo
90
- print(f"[Thread-{thread_id}] Error en solicitud {i+1}: {e}")
91
-
92
- thread_end_time = time.time()
93
- thread_duration = thread_end_time - thread_start_time
94
-
95
- print(f"[Thread-{thread_id}] Completado: {thread_successful} exitosas, {thread_failed} fallidas en {thread_duration:.2f}s")
96
-
97
- def run_load_test():
98
- """Función principal que ejecuta la prueba de carga"""
99
- global total_requests, successful_requests, failed_requests
100
-
101
- print(f"Iniciando prueba de carga...")
102
- print(f"- URL base: {BASE_URL}")
103
- print(f"- Endpoint: {ENDPOINT}")
104
- print(f"- Hilos concurrentes: {NUM_THREADS}")
105
- print(f"- Solicitudes por hilo: {REQUESTS_PER_THREAD}")
106
- print(f"- Duración: {DURATION_SECONDS} segundos")
107
- print(f"- Objetivo RPS: {TARGET_RPS}")
108
- print("-" * 50)
109
-
110
- start_time = time.time()
111
- end_time_target = start_time + DURATION_SECONDS
112
-
113
- # Crear y arrancar hilos
114
- threads = []
115
- for i in range(NUM_THREADS):
116
- thread = threading.Thread(target=make_requests_continuous, args=(i, end_time_target))
117
- threads.append(thread)
118
- thread.start()
119
-
120
- # Pequeño retraso entre la creación de hilos para evitar congestión
121
- time.sleep(0.001)
122
-
123
- # Esperar a que todos los hilos terminen
124
- for thread in threads:
125
- thread.join()
126
-
127
- actual_duration = time.time() - start_time
128
-
129
- # Mostrar resultados
130
- print("-" * 50)
131
- print("RESULTADOS DE LA PRUEBA DE CARGA:")
132
- print(f"Tiempo total: {actual_duration:.2f} segundos")
133
- print(f"Solicitudes totales: {total_requests}")
134
- print(f"Solicitudes exitosas: {successful_requests}")
135
- print(f"Solicitudes fallidas: {failed_requests}")
136
- print(f"Tasa de éxito: {(successful_requests/total_requests*100) if total_requests > 0 else 0:.2f}%")
137
- print(f"RPS Real (Solicitudes por segundo): {total_requests/actual_duration if actual_duration > 0 else 0:.2f}")
138
- print(f"RPS Objetivo: {TARGET_RPS}")
139
-
140
- if successful_requests > 0:
141
- print(f"RPS Exitoso (Solicitudes exitosas por segundo): {successful_requests/actual_duration if actual_duration > 0 else 0:.2f}")
142
-
143
- if __name__ == "__main__":
144
- try:
145
- run_load_test()
146
- except KeyboardInterrupt:
147
- print("\nPrueba interrumpida por el usuario.")
148
- print(f"Resultados parciales:")
149
- print(f"- Solicitudes totales: {total_requests}")
150
- print(f"- Solicitudes exitosas: {successful_requests}")
151
- print(f"- Solicitudes fallidas: {failed_requests}")
package/stress-test.js DELETED
@@ -1,190 +0,0 @@
1
- const http = require('http');
2
- const https = require('https');
3
- const url = require('url');
4
-
5
- class StressTester {
6
- constructor(balancerUrl, durationSeconds = 60, concurrentRequests = 10) {
7
- this.balancerUrl = balancerUrl;
8
- this.durationSeconds = durationSeconds;
9
- this.concurrentRequests = concurrentRequests;
10
- this.startTime = null;
11
- this.stats = {
12
- totalRequests: 0,
13
- successfulRequests: 0,
14
- failedRequests: 0,
15
- statusCodes: {},
16
- totalTime: 0,
17
- minTime: Infinity,
18
- maxTime: 0
19
- };
20
- this.running = false;
21
- }
22
-
23
- async makeRequest(path, method = 'GET', body = null) {
24
- const startTime = Date.now();
25
- let statusCode = 0;
26
- let success = false;
27
-
28
- return new Promise((resolve, reject) => {
29
- const parsedUrl = url.parse(`${this.balancerUrl}${path}`);
30
- const options = {
31
- hostname: parsedUrl.hostname,
32
- port: parsedUrl.port,
33
- path: parsedUrl.path,
34
- method: method,
35
- headers: {
36
- 'Content-Type': 'application/json'
37
- }
38
- };
39
-
40
- const request = http.request(options, (res) => {
41
- statusCode = res.statusCode;
42
- let data = '';
43
-
44
- res.on('data', (chunk) => {
45
- data += chunk;
46
- });
47
-
48
- res.on('end', () => {
49
- const endTime = Date.now();
50
- const responseTime = endTime - startTime;
51
-
52
- success = statusCode >= 200 && statusCode < 300;
53
-
54
- // Actualizar estadísticas
55
- this.stats.totalRequests++;
56
- if (success) {
57
- this.stats.successfulRequests++;
58
- } else {
59
- this.stats.failedRequests++;
60
- }
61
-
62
- if (!this.stats.statusCodes[statusCode]) {
63
- this.stats.statusCodes[statusCode] = 0;
64
- }
65
- this.stats.statusCodes[statusCode]++;
66
-
67
- this.stats.totalTime += responseTime;
68
- this.stats.minTime = Math.min(this.stats.minTime, responseTime);
69
- this.stats.maxTime = Math.max(this.stats.maxTime, responseTime);
70
-
71
- resolve({
72
- statusCode,
73
- responseTime,
74
- success
75
- });
76
- });
77
- });
78
-
79
- request.on('error', (err) => {
80
- const endTime = Date.now();
81
- const responseTime = endTime - startTime;
82
-
83
- this.stats.totalRequests++;
84
- this.stats.failedRequests++;
85
-
86
- if (!this.stats.statusCodes['ERROR']) {
87
- this.stats.statusCodes['ERROR'] = 0;
88
- }
89
- this.stats.statusCodes['ERROR']++;
90
-
91
- this.stats.totalTime += responseTime;
92
- this.stats.maxTime = Math.max(this.stats.maxTime, responseTime);
93
-
94
- resolve({
95
- statusCode: 'ERROR',
96
- responseTime,
97
- success: false,
98
- error: err.message
99
- });
100
- });
101
-
102
- if (body) {
103
- request.write(JSON.stringify(body));
104
- }
105
-
106
- request.end();
107
- });
108
- }
109
-
110
- async runTest() {
111
- console.log(`Iniciando prueba de estrés...`);
112
- console.log(`URL del balanceador: ${this.balancerUrl}`);
113
- console.log(`Duración: ${this.durationSeconds} segundos`);
114
- console.log(`Concurrencia: ${this.concurrentRequests} solicitudes simultáneas`);
115
- console.log('----------------------------------------');
116
-
117
- this.startTime = Date.now();
118
- this.running = true;
119
-
120
- // Lista de endpoints para probar
121
- const endpoints = [
122
- { path: '/api/endpoints', method: 'GET' },
123
- { path: '/sessions', method: 'GET' },
124
- { path: '/events', method: 'GET' },
125
- { path: '/reports/user-flow', method: 'GET' },
126
- { path: '/reports/product-views-by-country', method: 'GET' },
127
- { path: '/track/registerSession', method: 'POST', body: { test: 'stress_test_data' } },
128
- { path: '/track/registerEventA', method: 'POST', body: { event: 'test_event', data: 'test_data' } }
129
- ];
130
-
131
- // Función para hacer solicitudes continuamente durante la duración del test
132
- const makeRequests = async () => {
133
- while (this.running) {
134
- const randomEndpoint = endpoints[Math.floor(Math.random() * endpoints.length)];
135
- await this.makeRequest(randomEndpoint.path, randomEndpoint.method, randomEndpoint.body || null);
136
- }
137
- };
138
-
139
- // Iniciar múltiples promesas concurrentes
140
- const promises = [];
141
- for (let i = 0; i < this.concurrentRequests; i++) {
142
- promises.push(makeRequests());
143
- }
144
-
145
- // Esperar hasta que se cumpla la duración del test
146
- setTimeout(() => {
147
- this.running = false;
148
- }, this.durationSeconds * 1000);
149
-
150
- // Esperar a que todas las promesas terminen
151
- await Promise.all(promises);
152
-
153
- this.printResults();
154
- }
155
-
156
- printResults() {
157
- const totalTimeMs = Date.now() - this.startTime;
158
- const avgTime = this.stats.totalRequests > 0 ? this.stats.totalTime / this.stats.totalRequests : 0;
159
- const requestsPerSecond = this.stats.totalRequests / (totalTimeMs / 1000);
160
-
161
- console.log('\n========================================');
162
- console.log('RESULTADOS DE LA PRUEBA DE ESTRÉS');
163
- console.log('========================================');
164
- console.log(`Tiempo total de prueba: ${(totalTimeMs / 1000).toFixed(2)} segundos`);
165
- console.log(`Solicitudes totales: ${this.stats.totalRequests}`);
166
- console.log(`Solicitudes exitosas: ${this.stats.successfulRequests}`);
167
- console.log(`Solicitudes fallidas: ${this.stats.failedRequests}`);
168
- console.log(`RPS (Solicitudes por segundo): ${requestsPerSecond.toFixed(2)}`);
169
- console.log(`Tiempo promedio por solicitud: ${avgTime.toFixed(2)} ms`);
170
- console.log(`Tiempo mínimo: ${this.stats.minTime !== Infinity ? this.stats.minTime : 0} ms`);
171
- console.log(`Tiempo máximo: ${this.stats.maxTime} ms`);
172
- console.log('\nDistribución de códigos de estado:');
173
- Object.entries(this.stats.statusCodes).forEach(([code, count]) => {
174
- console.log(` ${code}: ${count} (${((count / this.stats.totalRequests) * 100).toFixed(2)}%)`);
175
- });
176
- console.log('========================================');
177
- }
178
- }
179
-
180
- // Ejecutar la prueba de estrés si se llama directamente
181
- if (require.main === module) {
182
- const balancerUrl = process.env.BALANCER_URL || 'http://localhost:8081';
183
- const duration = parseInt(process.env.DURATION_SECONDS) || 30;
184
- const concurrency = parseInt(process.env.CONCURRENT_REQUESTS) || 10;
185
-
186
- const tester = new StressTester(balancerUrl, duration, concurrency);
187
- tester.runTest().catch(console.error);
188
- }
189
-
190
- module.exports = { StressTester };