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.
package/index.js ADDED
@@ -0,0 +1,288 @@
1
+ // packages/cluster/index.js
2
+ import ClusterManager from './src/core/ClusterManager.js';
3
+ import WorkerManager from './src/core/WorkerManager.js';
4
+ import HealthMonitor from './src/core/HealthMonitor.js';
5
+ import LoadBalancer from './src/core/LoadBalancer.js';
6
+ import { loadEnv } from './src/utils/env-loader.js';
7
+
8
+ /**
9
+ * AetherJS Cluster Module
10
+ * High-performance cluster management for Node.js applications
11
+ */
12
+ class ClusterModule {
13
+ constructor(config = {}) {
14
+ // 加载环境配置
15
+ this.env = loadEnv();
16
+
17
+ // 合并配置
18
+ this.config = {
19
+ enabled: this.env.CLUSTER_ENABLED !== 'false',
20
+ workers: this.env.CLUSTER_WORKERS || 'auto',
21
+ port: parseInt(this.env.CLUSTER_PORT) || 3000,
22
+ gracefulShutdown: this.env.CLUSTER_GRACEFUL_SHUTDOWN !== 'false',
23
+ restartOnExit: this.env.CLUSTER_RESTART_ON_EXIT !== 'false',
24
+ restartDelay: parseInt(this.env.CLUSTER_RESTART_DELAY) || 1000,
25
+ healthCheck: {
26
+ enabled: this.env.CLUSTER_HEALTH_CHECK_ENABLED !== 'false',
27
+ interval: parseInt(this.env.CLUSTER_HEALTH_CHECK_INTERVAL) || 30000,
28
+ timeout: parseInt(this.env.CLUSTER_HEALTH_CHECK_TIMEOUT) || 5000
29
+ },
30
+ loadBalancing: {
31
+ enabled: this.env.CLUSTER_LOAD_BALANCING !== 'false',
32
+ type: this.env.CLUSTER_LOAD_BALANCER_TYPE || 'round-robin',
33
+ maxRequests: parseInt(this.env.CLUSTER_MAX_REQUESTS_PER_WORKER) || 1000
34
+ },
35
+ monitoring: {
36
+ enabled: this.env.CLUSTER_MONITORING_ENABLED !== 'false',
37
+ interval: parseInt(this.env.CLUSTER_MONITORING_INTERVAL) || 10000,
38
+ memoryThreshold: parseFloat(this.env.CLUSTER_MEMORY_THRESHOLD) || 0.8,
39
+ cpuThreshold: parseFloat(this.env.CLUSTER_CPU_THRESHOLD) || 0.7
40
+ },
41
+ ...config
42
+ };
43
+
44
+ this.clusterManager = null;
45
+ this.workerManager = null;
46
+ this.healthMonitor = null;
47
+ this.loadBalancer = null;
48
+ }
49
+
50
+ /**
51
+ * 初始化集群模块
52
+ * @param {Function} appFactory - 应用工厂函数
53
+ * @returns {Object|null} 应用实例或 null
54
+ */
55
+ initialize(appFactory) {
56
+ if (!this.config.enabled) {
57
+ console.log('🚫 Cluster module is disabled, running in single process mode');
58
+ return appFactory();
59
+ }
60
+
61
+ console.log('🚀 Initializing AetherJS Cluster Module...');
62
+
63
+ // 创建核心组件
64
+ this.clusterManager = new ClusterManager(this.config);
65
+ this.workerManager = new WorkerManager(this.config);
66
+ this.healthMonitor = new HealthMonitor(this.config.healthCheck);
67
+ this.loadBalancer = new LoadBalancer(this.config.loadBalancing);
68
+
69
+ // 启动集群
70
+ return this.clusterManager.start(() => {
71
+ // 工作进程中的逻辑
72
+ const app = appFactory();
73
+
74
+ // 设置工作进程应用
75
+ this.setupWorkerApp(app);
76
+
77
+ return app;
78
+ });
79
+ }
80
+
81
+ /**
82
+ * 设置工作进程应用
83
+ * @param {Object} app - 应用实例
84
+ */
85
+ setupWorkerApp(app) {
86
+ // 添加集群中间件
87
+ if (this.healthMonitor) {
88
+ app.use(this.healthMonitor.middleware());
89
+ }
90
+
91
+ // 添加集群路由
92
+ this.setupClusterRoutes(app);
93
+
94
+ // 设置进程间通信
95
+ this.setupIPC();
96
+
97
+ // 启动监控
98
+ this.startMonitoring();
99
+ }
100
+
101
+ /**
102
+ * 设置集群路由
103
+ * @param {Object} app - 应用实例
104
+ */
105
+ setupClusterRoutes(app) {
106
+ // 健康检查端点
107
+ app.get('/cluster/health', async (ctx) => {
108
+ const health = await this.healthMonitor.getHealthStatus();
109
+ ctx.body = health;
110
+ });
111
+
112
+ // 集群统计端点
113
+ app.get('/cluster/stats', (ctx) => {
114
+ const stats = this.workerManager.getStats();
115
+ ctx.body = {
116
+ status: 'ok',
117
+ timestamp: new Date().toISOString(),
118
+ stats: {
119
+ ...stats,
120
+ pid: process.pid,
121
+ isWorker: !process.env.isPrimary,
122
+ workerId: process.env.workerId,
123
+ uptime: process.uptime(),
124
+ memory: process.memoryUsage(),
125
+ cpu: process.cpuUsage()
126
+ }
127
+ };
128
+ });
129
+
130
+ // 工作进程管理端点
131
+ app.post('/cluster/workers/:pid/restart', (ctx) => {
132
+ const { pid } = ctx.params;
133
+ const success = this.clusterManager.restartWorker(parseInt(pid));
134
+
135
+ ctx.body = {
136
+ status: success ? 'ok' : 'error',
137
+ message: success ? `Worker ${pid} restart initiated` : `Worker ${pid} not found`,
138
+ pid
139
+ };
140
+ });
141
+
142
+ // 负载均衡信息
143
+ app.get('/cluster/load-balancer', (ctx) => {
144
+ const info = this.loadBalancer.getInfo();
145
+ ctx.body = info;
146
+ });
147
+ }
148
+
149
+ /**
150
+ * 设置进程间通信
151
+ */
152
+ setupIPC() {
153
+ process.on('message', (message) => {
154
+ switch (message.type) {
155
+ case 'SHUTDOWN':
156
+ this.handleShutdown(message);
157
+ break;
158
+ case 'RESTART':
159
+ this.handleRestart(message);
160
+ break;
161
+ case 'STATS_REQUEST':
162
+ this.handleStatsRequest(message);
163
+ break;
164
+ case 'HEALTH_CHECK':
165
+ this.handleHealthCheck(message);
166
+ break;
167
+ default:
168
+ console.log(`Received unknown message type: ${message.type}`);
169
+ }
170
+ });
171
+ }
172
+
173
+ /**
174
+ * 处理关闭信号
175
+ * @param {Object} message - IPC 消息
176
+ */
177
+ handleShutdown(message) {
178
+ console.log(`Worker ${process.pid} received shutdown signal`);
179
+
180
+ if (this.config.gracefulShutdown) {
181
+ // 执行优雅关闭
182
+ setTimeout(() => {
183
+ process.exit(0);
184
+ }, this.config.shutdownTimeout || 5000);
185
+ } else {
186
+ process.exit(0);
187
+ }
188
+ }
189
+
190
+ /**
191
+ * 处理重启信号
192
+ * @param {Object} message - IPC 消息
193
+ */
194
+ handleRestart(message) {
195
+ console.log(`Worker ${process.pid} received restart signal`);
196
+ process.exit(1); // 非0退出码会触发重启
197
+ }
198
+
199
+ /**
200
+ * 处理统计请求
201
+ * @param {Object} message - IPC 消息
202
+ */
203
+ handleStatsRequest(message) {
204
+ const stats = {
205
+ pid: process.pid,
206
+ memory: process.memoryUsage(),
207
+ cpu: process.cpuUsage(),
208
+ uptime: process.uptime(),
209
+ requests: this.workerManager ? this.workerManager.getRequestCount() : 0
210
+ };
211
+
212
+ process.send({
213
+ type: 'STATS_RESPONSE',
214
+ data: stats
215
+ });
216
+ }
217
+
218
+ /**
219
+ * 处理健康检查
220
+ * @param {Object} message - IPC 消息
221
+ */
222
+ handleHealthCheck(message) {
223
+ const health = {
224
+ pid: process.pid,
225
+ status: 'healthy',
226
+ timestamp: new Date().toISOString()
227
+ };
228
+
229
+ process.send({
230
+ type: 'HEALTH_RESPONSE',
231
+ data: health
232
+ });
233
+ }
234
+
235
+ /**
236
+ * 启动监控
237
+ */
238
+ startMonitoring() {
239
+ if (this.config.monitoring.enabled) {
240
+ setInterval(() => {
241
+ this.healthMonitor.check();
242
+ }, this.config.monitoring.interval);
243
+ }
244
+ }
245
+
246
+ /**
247
+ * 获取集群管理器
248
+ * @returns {ClusterManager} 集群管理器实例
249
+ */
250
+ getClusterManager() {
251
+ return this.clusterManager;
252
+ }
253
+
254
+ /**
255
+ * 获取工作进程管理器
256
+ * @returns {WorkerManager} 工作进程管理器实例
257
+ */
258
+ getWorkerManager() {
259
+ return this.workerManager;
260
+ }
261
+
262
+ /**
263
+ * 获取健康监控器
264
+ * @returns {HealthMonitor} 健康监控器实例
265
+ */
266
+ getHealthMonitor() {
267
+ return this.healthMonitor;
268
+ }
269
+
270
+ /**
271
+ * 获取负载均衡器
272
+ * @returns {LoadBalancer} 负载均衡器实例
273
+ */
274
+ getLoadBalancer() {
275
+ return this.loadBalancer;
276
+ }
277
+
278
+ /**
279
+ * 获取配置
280
+ * @returns {Object} 配置对象
281
+ */
282
+ getConfig() {
283
+ return this.config;
284
+ }
285
+ }
286
+
287
+ // 导出模块
288
+ export default ClusterModule;
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "aetherframework-cluster",
3
+ "version": "1.0.0",
4
+ "description": "Aether Framework - Cluster Management Module",
5
+ "main": "index.js",
6
+ "type": "module",
7
+
8
+ "scripts": {
9
+ "test": "jest",
10
+ "lint": "eslint .",
11
+ "coverage": "jest --coverage"
12
+ },
13
+ "keywords": [
14
+ "aether",
15
+ "framework",
16
+ "cluster",
17
+ "load-balancing",
18
+ "scalability"
19
+ ],
20
+ "author": "Aether Framework Team",
21
+ "license": "MIT",
22
+ "dependencies": {
23
+ "os": "^0.1.2"
24
+ },
25
+ "peerDependencies": {
26
+ "@aetherframework/core": "^1.0.0"
27
+ },
28
+ "engines": {
29
+ "node": ">=14.0.0"
30
+ },
31
+
32
+ "repository": {
33
+ "type": "git",
34
+ "url": "https://github.com/aetherjs/aetherframework-cluster.git",
35
+ "directory": "packages/cluster"
36
+ },
37
+ "bugs": {
38
+ "url": "https://github.com/aetherjs/aetherframework-cluster/issues"
39
+ },
40
+ "homepage": "https://github.com/aetherframework/aether-api#readme"
41
+ }
@@ -0,0 +1,109 @@
1
+ // package/cluster/core/ClusterManager.js
2
+ import cluster from 'cluster';
3
+ import EventEmitter from 'events';
4
+
5
+ export default class ClusterManager extends EventEmitter {
6
+ constructor(options = {}) {
7
+ super();
8
+
9
+ // 从环境变量读取配置,提供默认值
10
+ const defaultWorkers = process.env.CLUSTER_WORKERS === 'auto' ?
11
+ os.cpus().length - 1 : // 自动检测CPU核心数,保留1个给系统
12
+ parseInt(process.env.CLUSTER_WORKERS) || 7;
13
+
14
+ const defaultPort = parseInt(process.env.CLUSTER_PORT) || 3000;
15
+ const defaultGracefulShutdown = process.env.CLUSTER_GRACEFUL_SHUTDOWN !== 'false';
16
+ const defaultRestartOnExit = process.env.CLUSTER_RESTART_ON_EXIT !== 'false';
17
+ const defaultWorkerReusePort = process.env.CLUSTER_WORKER_REUSE_PORT === 'true';
18
+ const defaultEnableIPC = process.env.CLUSTER_ENABLE_IPC !== 'false';
19
+ const defaultIpcTimeout = parseInt(process.env.CLUSTER_IPC_TIMEOUT) || 5000;
20
+ const defaultShutdownTimeout = parseInt(process.env.CLUSTER_SHUTDOWN_TIMEOUT) || 10000;
21
+
22
+ this.options = {
23
+ workers: defaultWorkers,
24
+ port: defaultPort,
25
+ gracefulShutdown: defaultGracefulShutdown,
26
+ restartOnExit: defaultRestartOnExit,
27
+ workerReusePort: defaultWorkerReusePort,
28
+ enableIPC: defaultEnableIPC,
29
+ ipcTimeout: defaultIpcTimeout,
30
+ shutdownTimeout: defaultShutdownTimeout,
31
+ ...options
32
+ };
33
+
34
+ this.readyCount = 0;
35
+ this.isShuttingDown = false;
36
+
37
+ if (cluster.isPrimary) this.setupMaster();
38
+ }
39
+
40
+ setupMaster() {
41
+ console.log(`🚀 Master process ${process.pid} is running`);
42
+ console.log(`📊 Configuration: workers=${this.options.workers}, port=${this.options.port}`);
43
+ console.log(`⚙️ Settings: gracefulShutdown=${this.options.gracefulShutdown}, restartOnExit=${this.options.restartOnExit}`);
44
+
45
+ cluster.on('fork', (worker) => {
46
+ console.log(`🔄 Worker ${worker.id} (PID: ${worker.process.pid}) forked`);
47
+ });
48
+
49
+ cluster.on('online', (worker) => {
50
+ console.log(`✅ Worker ${worker.process.pid} is online`);
51
+ });
52
+
53
+ cluster.on('message', (worker, message) => {
54
+ if (message?.type === 'READY') {
55
+ this.readyCount++;
56
+ console.log(`🚀 Worker ${message.pid} is ready (${this.readyCount}/${this.options.workers})`);
57
+ this.emit('worker:ready', message);
58
+ }
59
+ });
60
+
61
+ cluster.on('exit', (worker, code) => {
62
+ console.log(`❌ Worker ${worker.process.pid} exited (${code})`);
63
+ if (!this.isShuttingDown && this.options.restartOnExit) {
64
+ console.log(`🔄 Restarting worker ${worker.process.pid}`);
65
+ cluster.fork({ PORT: this.options.port.toString() });
66
+ }
67
+ });
68
+ }
69
+
70
+ start() {
71
+ if (!cluster.isPrimary) return;
72
+
73
+ console.log(`📊 Starting ${this.options.workers} workers on port ${this.options.port}`);
74
+
75
+ for (let i = 0; i < this.options.workers; i++) {
76
+ cluster.fork({
77
+ PORT: this.options.port.toString(),
78
+ WORKER_ID: i + 1,
79
+ IS_WORKER: 'true'
80
+ });
81
+ }
82
+
83
+ return this;
84
+ }
85
+
86
+ stop() {
87
+ if (!cluster.isPrimary) return;
88
+
89
+ this.isShuttingDown = true;
90
+ console.log('⚠️ Received shutdown signal, initiating shutdown...');
91
+
92
+ const workers = Object.values(cluster.workers || {});
93
+ console.log(`📤 Sending shutdown signal to ${workers.length} workers`);
94
+
95
+ workers.forEach(worker => {
96
+ if (worker.isConnected()) {
97
+ worker.send({ type: 'SHUTDOWN' });
98
+ }
99
+ });
100
+
101
+ return new Promise((resolve) => {
102
+ setTimeout(() => {
103
+ console.log('✅ All workers shut down successfully');
104
+ console.log('👋 Master process exiting');
105
+ resolve();
106
+ }, this.options.shutdownTimeout);
107
+ });
108
+ }
109
+ }