kukuy 1.4.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 (41) hide show
  1. package/.ctagsd/ctagsd.json +954 -0
  2. package/.ctagsd/file_list.txt +100 -0
  3. package/.ctagsd/tags.db +0 -0
  4. package/CHANGELOG.md +101 -0
  5. package/LICENSE +680 -0
  6. package/README.md +251 -0
  7. package/captura.png +0 -0
  8. package/kukuy.js +23 -0
  9. package/kukuy.workspace +11 -0
  10. package/package.json +26 -0
  11. package/restart-balancer.sh +10 -0
  12. package/routes.json +14 -0
  13. package/scripts/load_test.py +151 -0
  14. package/servers.json +19 -0
  15. package/servers_real.json +19 -0
  16. package/src/algorithms/AlgorithmManager.js +85 -0
  17. package/src/algorithms/IPHashAlgorithm.js +131 -0
  18. package/src/algorithms/LoadBalancingAlgorithm.js +23 -0
  19. package/src/algorithms/RoundRobinAlgorithm.js +67 -0
  20. package/src/config/ConfigManager.js +37 -0
  21. package/src/config/RouteLoader.js +36 -0
  22. package/src/core/Balancer.js +353 -0
  23. package/src/core/RoundRobinAlgorithm.js +60 -0
  24. package/src/core/ServerPool.js +77 -0
  25. package/src/dashboard/WebDashboard.js +150 -0
  26. package/src/dashboard/WebSocketServer.js +114 -0
  27. package/src/extensibility/CachingFilter.js +134 -0
  28. package/src/extensibility/FilterChain.js +93 -0
  29. package/src/extensibility/HookManager.js +48 -0
  30. package/src/protocol/HttpBalancer.js +37 -0
  31. package/src/protocol/HttpsBalancer.js +47 -0
  32. package/src/utils/BalancerLogger.js +102 -0
  33. package/src/utils/HealthChecker.js +51 -0
  34. package/src/utils/Logger.js +39 -0
  35. package/src/utils/MetricsCollector.js +82 -0
  36. package/src/utils/ProfessionalMetrics.js +501 -0
  37. package/start-iphash.sh +5 -0
  38. package/start-roundrobin.sh +5 -0
  39. package/stress-test.js +190 -0
  40. package/webpage/README.md +17 -0
  41. package/webpage/index.html +549 -0
@@ -0,0 +1,37 @@
1
+ const http = require('http');
2
+ const https = require('https');
3
+ const fs = require('fs');
4
+ const { Logger } = require('../utils/Logger');
5
+
6
+ class HttpBalancer {
7
+ constructor(port, requestHandler) {
8
+ this.port = port;
9
+ this.requestHandler = requestHandler;
10
+ this.logger = new Logger();
11
+ this.server = null;
12
+ }
13
+
14
+ start() {
15
+ this.server = http.createServer((req, res) => {
16
+ this.requestHandler(req, res);
17
+ });
18
+
19
+ this.server.listen(this.port, () => {
20
+ this.logger.info(`Balanceador HTTP escuchando en puerto ${this.port}`);
21
+ });
22
+
23
+ return this.server;
24
+ }
25
+
26
+ stop() {
27
+ if (this.server) {
28
+ this.server.close();
29
+ }
30
+ }
31
+
32
+ isRunning() {
33
+ return this.server && this.server.listening;
34
+ }
35
+ }
36
+
37
+ module.exports = { HttpBalancer };
@@ -0,0 +1,47 @@
1
+ const https = require('https');
2
+ const fs = require('fs');
3
+ const { Logger } = require('../utils/Logger');
4
+
5
+ class HttpsBalancer {
6
+ constructor(port, sslCertPath, sslKeyPath, requestHandler) {
7
+ this.port = port;
8
+ this.sslCertPath = sslCertPath;
9
+ this.sslKeyPath = sslKeyPath;
10
+ this.requestHandler = requestHandler;
11
+ this.logger = new Logger();
12
+ this.server = null;
13
+ }
14
+
15
+ start() {
16
+ if (!this.sslCertPath || !this.sslKeyPath) {
17
+ throw new Error('Rutas al certificado SSL y llave privada son requeridas para HTTPS');
18
+ }
19
+
20
+ const options = {
21
+ cert: fs.readFileSync(this.sslCertPath),
22
+ key: fs.readFileSync(this.sslKeyPath)
23
+ };
24
+
25
+ this.server = https.createServer(options, (req, res) => {
26
+ this.requestHandler(req, res);
27
+ });
28
+
29
+ this.server.listen(this.port, () => {
30
+ this.logger.info(`Balanceador HTTPS escuchando en puerto ${this.port}`);
31
+ });
32
+
33
+ return this.server;
34
+ }
35
+
36
+ stop() {
37
+ if (this.server) {
38
+ this.server.close();
39
+ }
40
+ }
41
+
42
+ isRunning() {
43
+ return this.server && this.server.listening;
44
+ }
45
+ }
46
+
47
+ module.exports = { HttpsBalancer };
@@ -0,0 +1,102 @@
1
+ const fs = require('fs');
2
+ const { ConfigManager } = require('../config/ConfigManager');
3
+
4
+ class BalancerLogger {
5
+ constructor() {
6
+ this.config = ConfigManager.getInstance();
7
+ this.logLevel = this.config.logLevel || 'info';
8
+ this.logFile = process.env.LOG_FILE_PATH || './balancer.log';
9
+ this.levels = {
10
+ error: 0,
11
+ warn: 1,
12
+ info: 2,
13
+ debug: 3
14
+ };
15
+ }
16
+
17
+ log(level, message, metadata = {}) {
18
+ if (this.levels[level] <= this.levels[this.logLevel]) {
19
+ const timestamp = new Date().toISOString();
20
+ const logEntry = {
21
+ timestamp,
22
+ level: level.toUpperCase(),
23
+ message,
24
+ ...metadata
25
+ };
26
+
27
+ const logLine = JSON.stringify(logEntry) + '\n';
28
+
29
+ // Imprimir en consola
30
+ console.log(`[${timestamp}] ${level.toUpperCase()}: ${message}`, metadata);
31
+
32
+ // Escribir en archivo de log
33
+ try {
34
+ fs.appendFileSync(this.logFile, logLine);
35
+ } catch (err) {
36
+ console.error('Error escribiendo en archivo de log:', err.message);
37
+ }
38
+ }
39
+ }
40
+
41
+ error(message, metadata = {}) {
42
+ this.log('error', message, metadata);
43
+ }
44
+
45
+ warn(message, metadata = {}) {
46
+ this.log('warn', message, metadata);
47
+ }
48
+
49
+ info(message, metadata = {}) {
50
+ this.log('info', message, metadata);
51
+ }
52
+
53
+ debug(message, metadata = {}) {
54
+ this.log('debug', message, metadata);
55
+ }
56
+
57
+ // Métodos específicos para el balanceador
58
+ logOnlineTarget(server, requestInfo = {}) {
59
+ this.info(`Servidor ONLINE - ID: ${server.id}, URL: ${server.url}`, {
60
+ eventType: 'target_online',
61
+ serverId: server.id,
62
+ serverUrl: server.url,
63
+ request: requestInfo,
64
+ timestamp: new Date().toISOString()
65
+ });
66
+ }
67
+
68
+ logOfflineTarget(server, error, requestInfo = {}) {
69
+ this.warn(`Servidor OFFLINE - ID: ${server.id}, URL: ${server.url}, Error: ${error.message}`, {
70
+ eventType: 'target_offline',
71
+ serverId: server.id,
72
+ serverUrl: server.url,
73
+ error: error.message,
74
+ request: requestInfo,
75
+ timestamp: new Date().toISOString()
76
+ });
77
+ }
78
+
79
+ logTargetSelection(server, algorithm = 'round_robin') {
80
+ this.debug(`Seleccionado servidor destino - ID: ${server.id}, URL: ${server.url}`, {
81
+ eventType: 'target_selected',
82
+ serverId: server.id,
83
+ serverUrl: server.url,
84
+ algorithm,
85
+ timestamp: new Date().toISOString()
86
+ });
87
+ }
88
+
89
+ logLoadBalancingStats(stats) {
90
+ this.info('Estadísticas de balanceo de carga', {
91
+ eventType: 'balancing_stats',
92
+ totalRequests: stats.totalRequests,
93
+ successfulRequests: stats.successfulRequests,
94
+ failedRequests: stats.failedRequests,
95
+ onlineTargets: stats.onlineTargets,
96
+ offlineTargets: stats.offlineTargets,
97
+ timestamp: new Date().toISOString()
98
+ });
99
+ }
100
+ }
101
+
102
+ module.exports = { BalancerLogger };
@@ -0,0 +1,51 @@
1
+ const http = require('http');
2
+ const https = require('https');
3
+ const url = require('url');
4
+
5
+ class HealthChecker {
6
+ constructor() {
7
+ this.timeout = 5000; // 5 segundos de timeout
8
+ }
9
+
10
+ async checkServerHealth(server) {
11
+ try {
12
+ const parsedUrl = url.parse(server.url);
13
+ const options = {
14
+ hostname: parsedUrl.hostname,
15
+ port: parsedUrl.port,
16
+ path: '/health', // Ruta estándar para verificación de salud
17
+ method: 'GET',
18
+ timeout: this.timeout
19
+ };
20
+
21
+ return new Promise((resolve) => {
22
+ const request = server.protocol === 'https:'
23
+ ? https.request(options)
24
+ : http.request(options);
25
+
26
+ request.on('response', (res) => {
27
+ // Considerar saludable si obtenemos una respuesta exitosa
28
+ const isHealthy = res.statusCode >= 200 && res.statusCode < 400;
29
+ resolve(isHealthy);
30
+ });
31
+
32
+ request.on('error', (err) => {
33
+ console.error(`Error verificando salud de ${server.url}: ${err.message}`);
34
+ resolve(false);
35
+ });
36
+
37
+ request.on('timeout', () => {
38
+ console.error(`Timeout verificando salud de ${server.url}`);
39
+ resolve(false);
40
+ });
41
+
42
+ request.end();
43
+ });
44
+ } catch (error) {
45
+ console.error(`Error en la verificación de salud: ${error.message}`);
46
+ return false;
47
+ }
48
+ }
49
+ }
50
+
51
+ module.exports = { HealthChecker };
@@ -0,0 +1,39 @@
1
+ const { ConfigManager } = require('../config/ConfigManager');
2
+
3
+ class Logger {
4
+ constructor() {
5
+ this.config = ConfigManager.getInstance();
6
+ this.level = this.config.logLevel;
7
+ this.levels = {
8
+ error: 0,
9
+ warn: 1,
10
+ info: 2,
11
+ debug: 3
12
+ };
13
+ }
14
+
15
+ log(level, message) {
16
+ if (this.levels[level] <= this.levels[this.level]) {
17
+ const timestamp = new Date().toISOString();
18
+ console.log(`[${timestamp}] ${level.toUpperCase()}: ${message}`);
19
+ }
20
+ }
21
+
22
+ error(message) {
23
+ this.log('error', message);
24
+ }
25
+
26
+ warn(message) {
27
+ this.log('warn', message);
28
+ }
29
+
30
+ info(message) {
31
+ this.log('info', message);
32
+ }
33
+
34
+ debug(message) {
35
+ this.log('debug', message);
36
+ }
37
+ }
38
+
39
+ module.exports = { Logger };
@@ -0,0 +1,82 @@
1
+ class MetricsCollector {
2
+ constructor() {
3
+ this.metrics = {
4
+ totalRequests: 0,
5
+ successfulRequests: 0,
6
+ failedRequests: 0,
7
+ totalResponseTime: 0,
8
+ minResponseTime: Infinity,
9
+ maxResponseTime: 0,
10
+ startTime: Date.now(),
11
+ requestsByPath: {},
12
+ requestsByServer: {}
13
+ };
14
+ }
15
+
16
+ recordRequest(path, serverId, responseTime, success = true) {
17
+ this.metrics.totalRequests++;
18
+
19
+ if (success) {
20
+ this.metrics.successfulRequests++;
21
+ } else {
22
+ this.metrics.failedRequests++;
23
+ }
24
+
25
+ this.metrics.totalResponseTime += responseTime;
26
+
27
+ if (responseTime < this.metrics.minResponseTime) {
28
+ this.metrics.minResponseTime = responseTime;
29
+ }
30
+
31
+ if (responseTime > this.metrics.maxResponseTime) {
32
+ this.metrics.maxResponseTime = responseTime;
33
+ }
34
+
35
+ // Contar solicitudes por ruta
36
+ if (!this.metrics.requestsByPath[path]) {
37
+ this.metrics.requestsByPath[path] = 0;
38
+ }
39
+ this.metrics.requestsByPath[path]++;
40
+
41
+ // Contar solicitudes por servidor
42
+ if (!this.metrics.requestsByServer[serverId]) {
43
+ this.metrics.requestsByServer[serverId] = 0;
44
+ }
45
+ this.metrics.requestsByServer[serverId]++;
46
+ }
47
+
48
+ getMetrics() {
49
+ const uptime = Date.now() - this.metrics.startTime;
50
+ const avgResponseTime = this.metrics.totalRequests > 0
51
+ ? this.metrics.totalResponseTime / this.metrics.totalRequests
52
+ : 0;
53
+
54
+ const rps = uptime > 0
55
+ ? (this.metrics.totalRequests / uptime) * 1000
56
+ : 0;
57
+
58
+ return {
59
+ ...this.metrics,
60
+ uptime,
61
+ avgResponseTime,
62
+ rps,
63
+ minResponseTime: this.metrics.minResponseTime === Infinity ? 0 : this.metrics.minResponseTime
64
+ };
65
+ }
66
+
67
+ reset() {
68
+ this.metrics = {
69
+ totalRequests: 0,
70
+ successfulRequests: 0,
71
+ failedRequests: 0,
72
+ totalResponseTime: 0,
73
+ minResponseTime: Infinity,
74
+ maxResponseTime: 0,
75
+ startTime: Date.now(),
76
+ requestsByPath: {},
77
+ requestsByServer: {}
78
+ };
79
+ }
80
+ }
81
+
82
+ module.exports = { MetricsCollector };