aetherframework-cluster 1.0.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.
@@ -0,0 +1,150 @@
1
+ // packages/cluster/src/examples/advanced-cluster.js
2
+ import path from 'path';
3
+ import { fileURLToPath } from 'url';
4
+ import http from 'http';
5
+ import cluster from 'cluster';
6
+ import ClusterManager from '../core/ClusterManager.js';
7
+ import CPUDetector from '../utils/cpu-detector.js';
8
+
9
+ const __filename = fileURLToPath(import.meta.url);
10
+ const __dirname = path.dirname(__filename);
11
+
12
+ function createApp() {
13
+ let requestCount = 0;
14
+
15
+ const server = http.createServer((req, res) => {
16
+ requestCount++;
17
+
18
+ console.log(`[Worker ${process.pid}] Received request #${requestCount}: ${req.method} ${req.url}`);
19
+
20
+ const responseData = {
21
+ status: 'ok',
22
+ pid: process.pid,
23
+ workerId: process.env.WORKER_ID || 'unknown',
24
+ requestCount: requestCount,
25
+ message: 'Hello from Cluster Worker',
26
+ timestamp: new Date().toISOString(),
27
+ url: req.url,
28
+ method: req.method
29
+ };
30
+
31
+ res.writeHead(200, {
32
+ 'Content-Type': 'application/json',
33
+ 'X-Worker-PID': process.pid,
34
+ 'X-Worker-ID': process.env.WORKER_ID || 'unknown'
35
+ });
36
+
37
+ res.end(JSON.stringify(responseData, null, 2));
38
+
39
+ if (process.send) {
40
+ process.send({ type: 'REQUEST_PROCESSED' });
41
+ }
42
+ });
43
+
44
+ // 【Key fix】Use flag to avoid duplicate shutdown
45
+ let isShuttingDown = false;
46
+
47
+ const gracefulShutdown = () => {
48
+ if (isShuttingDown) return;
49
+ isShuttingDown = true;
50
+
51
+ console.log(`Worker ${process.pid} starting graceful shutdown...`);
52
+
53
+ server.close(() => {
54
+ console.log(`Worker ${process.pid} HTTP server closed`);
55
+ process.exit(0);
56
+ });
57
+
58
+ // Set shutdown timeout
59
+ setTimeout(() => {
60
+ console.log(`Worker ${process.pid} shutdown timeout, forcing exit`);
61
+ process.exit(1);
62
+ }, 5000);
63
+ };
64
+
65
+ // Listen for shutdown messages from master process
66
+ process.on('message', (message) => {
67
+ if (message.type === 'SHUTDOWN') {
68
+ console.log(`Worker ${process.pid} received shutdown signal from master`);
69
+ gracefulShutdown();
70
+ }
71
+ });
72
+
73
+ // Listen for system signals (as backup)
74
+ process.once('SIGTERM', () => {
75
+ console.log(`Worker ${process.pid} received SIGTERM`);
76
+ gracefulShutdown();
77
+ });
78
+
79
+ process.once('SIGINT', () => {
80
+ console.log(`Worker ${process.pid} received SIGINT`);
81
+ gracefulShutdown();
82
+ });
83
+
84
+ return {
85
+ handleRequest: async (requestData, responseCallback) => {
86
+ const result = {
87
+ pid: process.pid,
88
+ workerId: process.env.WORKER_ID,
89
+ data: requestData,
90
+ processedAt: new Date().toISOString()
91
+ };
92
+
93
+ if (responseCallback) {
94
+ responseCallback(result);
95
+ }
96
+
97
+ return result;
98
+ },
99
+
100
+ getStats: () => ({
101
+ pid: process.pid,
102
+ requestCount: requestCount,
103
+ uptime: process.uptime(),
104
+ memoryUsage: process.memoryUsage()
105
+ }),
106
+
107
+ close: (callback) => {
108
+ server.close(callback);
109
+ }
110
+ };
111
+ }
112
+
113
+ async function main() {
114
+ console.log('🔧 Initializing Advanced Cluster Configuration...');
115
+
116
+ const cpuInfo = CPUDetector.getInfo();
117
+ const optimalWorkers = CPUDetector.getOptimalWorkerCount({ reserve: 1 });
118
+
119
+ console.log(`🖥️ Detected ${cpuInfo.count} cores. Using ${optimalWorkers} workers`);
120
+
121
+ const clusterManager = new ClusterManager({
122
+ workers: optimalWorkers,
123
+ port: 3000,
124
+ gracefulShutdown: true,
125
+ restartOnExit: true,
126
+ restartDelay: 1000,
127
+ shutdownTimeout: 10000
128
+ });
129
+
130
+ // Note: No longer listening for events here, as ClusterManager handles logging internally
131
+ // If custom event handling is needed, add it here but ensure no duplication
132
+
133
+ // Start the cluster
134
+ await clusterManager.start(createApp);
135
+
136
+ console.log('✅ Cluster is running. Press Ctrl+C to stop.');
137
+
138
+ // Note: No longer setting SignalHandler here, as ClusterManager handles signals internally
139
+ // Avoid signal handling conflicts
140
+ }
141
+
142
+ // Execute only in master process
143
+ if (cluster.isPrimary) {
144
+ main().catch((err) => {
145
+ console.error('Failed to start cluster:', err);
146
+ process.exit(1);
147
+ });
148
+ } else {
149
+ // Worker processes don't execute any code, handled by ClusterManager.startWorker()
150
+ }
@@ -0,0 +1,107 @@
1
+ // packages/cluster/examples/basic-cluster-simple.js
2
+ import http from 'http';
3
+ import cluster from 'cluster';
4
+ import ClusterManager from '../core/ClusterManager.js';
5
+
6
+ /**
7
+ * Simple application factory function
8
+ */
9
+ function createSimpleApp() {
10
+ const server = http.createServer((req, res) => {
11
+ res.writeHead(200, { 'Content-Type': 'application/json' });
12
+ res.end(JSON.stringify({
13
+ pid: process.pid,
14
+ message: 'Hello from Simple Cluster',
15
+ timestamp: new Date().toISOString()
16
+ }));
17
+ });
18
+
19
+ // 【Key fix】Use flag to avoid duplicate shutdown, consistent with advanced-cluster.js
20
+ let isShuttingDown = false;
21
+
22
+ const gracefulShutdown = () => {
23
+ if (isShuttingDown) return;
24
+ isShuttingDown = true;
25
+
26
+ console.log(`Worker ${process.pid} starting graceful shutdown...`);
27
+
28
+ server.close(() => {
29
+ console.log(`Worker ${process.pid} server closed`);
30
+ process.exit(0);
31
+ });
32
+
33
+ // Set shutdown timeout
34
+ setTimeout(() => {
35
+ console.log(`Worker ${process.pid} shutdown timeout, forcing exit`);
36
+ process.exit(1);
37
+ }, 5000);
38
+ };
39
+
40
+ // Listen for shutdown messages from master process
41
+ process.on('message', (message) => {
42
+ if (message.type === 'SHUTDOWN') {
43
+ console.log(`Worker ${process.pid} received shutdown signal from master`);
44
+ gracefulShutdown();
45
+ }
46
+ });
47
+
48
+ // Listen for system signals (as backup)
49
+ process.once('SIGTERM', () => {
50
+ console.log(`Worker ${process.pid} received SIGTERM`);
51
+ gracefulShutdown();
52
+ });
53
+
54
+ process.once('SIGINT', () => {
55
+ console.log(`Worker ${process.pid} received SIGINT`);
56
+ gracefulShutdown();
57
+ });
58
+
59
+ return {
60
+ handleRequest: async (data, callback) => {
61
+ if (callback) {
62
+ callback({ pid: process.pid, data });
63
+ }
64
+ return { pid: process.pid, data };
65
+ },
66
+
67
+ close: (callback) => {
68
+ server.close(callback);
69
+ }
70
+ };
71
+ }
72
+
73
+
74
+ async function main() {
75
+ console.log('🚀 Starting Simple Cluster Example...');
76
+
77
+ const cluster = new ClusterManager({
78
+ workers: 2, // Fixed 2 worker processes
79
+ port: 3000,
80
+ gracefulShutdown: true,
81
+ shutdownTimeout: 10000
82
+ });
83
+
84
+ // Basic event listeners
85
+ cluster.on('worker:ready', (data) => {
86
+ console.log(`Worker ${data.pid} ready`);
87
+ });
88
+
89
+ cluster.on('cluster:ready', (data) => {
90
+ console.log(`Cluster ready on port ${data.port}`);
91
+ });
92
+
93
+ // Listen for cluster shutdown events
94
+ cluster.on('cluster:shutdown', () => {
95
+ console.log('Cluster shutdown complete');
96
+ });
97
+
98
+ await cluster.start(createSimpleApp);
99
+
100
+ console.log('✅ Cluster running. Press Ctrl+C to stop.');
101
+
102
+
103
+ }
104
+
105
+ if (cluster.isPrimary) {
106
+ main().catch(console.error);
107
+ }
@@ -0,0 +1,112 @@
1
+ // package/cluster/src/examples/benchmark-cluster.js
2
+ import http from 'http';
3
+ import net from 'net';
4
+ import cluster from 'cluster';
5
+ import os from 'os';
6
+ import { Worker } from 'worker_threads';
7
+ import ClusterManager from '../core/ClusterManager.js';
8
+
9
+ if (!cluster.isPrimary) {
10
+ const CORES = os.availableParallelism?.() || os.cpus().length;
11
+ const workerPool = [];
12
+ const callbacks = new Map();
13
+ let nextId = 0;
14
+ let workerIndex = 0;
15
+
16
+ console.log(`[Worker ${process.pid}] Starting ${CORES} persistent Worker Threads`);
17
+
18
+ for (let i = 0; i < CORES; i++) {
19
+ const worker = new Worker(`
20
+ const { parentPort } = require('worker_threads');
21
+ parentPort.on('message', ({ id }) => {
22
+ let sum = 0;
23
+ for (let i = 0; i < 60000; i++) { // Further reduce computation, target 4000+ req/s
24
+ sum += Math.random() * 0.1;
25
+ }
26
+ parentPort.postMessage({ id, result: sum.toFixed(2) });
27
+ });
28
+ `, { eval: true });
29
+
30
+ worker.on('message', ({ id, result }) => {
31
+ const cb = callbacks.get(id);
32
+ if (cb) {
33
+ cb(result);
34
+ callbacks.delete(id);
35
+ }
36
+ });
37
+
38
+ workerPool.push(worker);
39
+ }
40
+
41
+ function heavyComputation() {
42
+ return new Promise(resolve => {
43
+ const id = nextId++;
44
+ callbacks.set(id, resolve);
45
+ const worker = workerPool[workerIndex];
46
+ workerIndex = (workerIndex + 1) % CORES;
47
+ worker.postMessage({ id });
48
+ });
49
+ }
50
+
51
+ const server = http.createServer(async (req, res) => {
52
+ const start = process.hrtime.bigint();
53
+
54
+ if (req.url === '/heavy') {
55
+ await heavyComputation();
56
+ const latencyMs = Number(process.hrtime.bigint() - start) / 1_000_000;
57
+
58
+ res.writeHead(200, {
59
+ 'Content-Type': 'application/json',
60
+ 'Server': 'HighPerf-WorkerPool'
61
+ });
62
+ res.end(`{"ok":true,"ms":${latencyMs.toFixed(2)}}`);
63
+ return;
64
+ }
65
+
66
+ res.writeHead(200, { 'Content-Type': 'application/json' });
67
+ res.end('{"status":"light"}');
68
+ });
69
+
70
+ server.listen(3005, '127.0.0.1', () => {
71
+ console.log(`✅ Worker ${process.pid} listening on http://127.0.0.1:3005`);
72
+ if (process.send) process.send({ type: 'READY', pid: process.pid });
73
+ });
74
+ }
75
+
76
+ // ====================== Master Process ======================
77
+ async function isPortAvailable(port) {
78
+ return new Promise(r => {
79
+ const s = net.createServer()
80
+ .once('error', () => r(false))
81
+ .once('listening', () => { s.close(); r(true); })
82
+ .listen(port, '127.0.0.1');
83
+ });
84
+ }
85
+
86
+ async function main() {
87
+ console.log('🚀 Starting final high-performance version (Single Process + Optimized Thread Pool)...');
88
+
89
+ if (!(await isPortAvailable(3005))) {
90
+ console.error('❌ Port 3005 is already in use');
91
+ process.exit(1);
92
+ }
93
+
94
+ const manager = new ClusterManager({
95
+ workers: 1,
96
+ port: 3005
97
+ });
98
+
99
+ manager.on('worker:ready', () => {
100
+ console.log('\n🎉 Service is ready! Recommended benchmark:');
101
+ console.log('autocannon -c 1000 -d 30 -p 10 http://127.0.0.1:3005/heavy\n');
102
+ });
103
+
104
+ manager.start();
105
+ }
106
+
107
+ if (cluster.isPrimary) {
108
+ main().catch(err => {
109
+ console.error('Startup failed:', err);
110
+ process.exit(1);
111
+ });
112
+ }
@@ -0,0 +1,52 @@
1
+ // packages/cluster/src/examples/simple-app.js
2
+ import http from 'http';
3
+
4
+ /**
5
+ * Application factory function
6
+ * This function is called when each Worker process starts to create a service instance
7
+ */
8
+ export default function createApp() {
9
+ // 1. Create HTTP server
10
+ const server = http.createServer((req, res) => {
11
+ // Log request
12
+ console.log(`[Worker ${process.pid}] Received request: ${req.method} ${req.url}`);
13
+
14
+ // Build response data
15
+ const responseData = {
16
+ status: 'ok',
17
+ pid: process.pid,
18
+ workerId: process.env.WORKER_ID || 'unknown',
19
+ message: 'Hello from Cluster Worker',
20
+ timestamp: new Date().toISOString(),
21
+ url: req.url
22
+ };
23
+
24
+ // Send response
25
+ res.writeHead(200, { 'Content-Type': 'application/json' });
26
+ res.end(JSON.stringify(responseData, null, 2));
27
+ });
28
+
29
+ // 2. Return object containing handleRequest method
30
+ // ClusterManager expects to interact with your application this way
31
+ return {
32
+ server, // Keep server reference for later closing
33
+
34
+ // ClusterManager will call this method to handle incoming requests or start listening
35
+ handleRequest: (req, res) => {
36
+ server.emit('request', req, res);
37
+ },
38
+
39
+ // Optional: Method to start listening on a port
40
+ listen: (port, callback) => {
41
+ server.listen(port, () => {
42
+ console.log(`[Worker ${process.pid}] Server listening on port ${port}`);
43
+ if (callback) callback();
44
+ });
45
+ },
46
+
47
+ // Optional: Method to close the server
48
+ close: (callback) => {
49
+ server.close(callback);
50
+ }
51
+ };
52
+ }