@eggjs/cluster 4.0.0-beta.34 → 4.0.0-beta.36

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 (35) hide show
  1. package/dist/agent_worker.d.ts +1 -1
  2. package/dist/agent_worker.js +50 -65
  3. package/dist/app_worker.d.ts +1 -1
  4. package/dist/app_worker.js +127 -165
  5. package/dist/error/ClusterAgentWorkerError.d.ts +12 -9
  6. package/dist/error/ClusterAgentWorkerError.js +22 -19
  7. package/dist/error/ClusterWorkerExceptionError.d.ts +9 -6
  8. package/dist/error/ClusterWorkerExceptionError.js +17 -14
  9. package/dist/index.d.ts +20 -15
  10. package/dist/index.js +20 -16
  11. package/dist/master.d.ts +89 -86
  12. package/dist/master.js +425 -556
  13. package/dist/utils/messenger.d.ts +93 -89
  14. package/dist/utils/messenger.js +143 -178
  15. package/dist/utils/mode/base/agent.d.ts +42 -35
  16. package/dist/utils/mode/base/agent.js +63 -65
  17. package/dist/utils/mode/base/app.d.ts +53 -45
  18. package/dist/utils/mode/base/app.js +77 -80
  19. package/dist/utils/mode/impl/process/agent.d.ts +20 -16
  20. package/dist/utils/mode/impl/process/agent.js +95 -105
  21. package/dist/utils/mode/impl/process/app.d.ts +23 -19
  22. package/dist/utils/mode/impl/process/app.js +116 -118
  23. package/dist/utils/mode/impl/worker_threads/agent.d.ts +20 -16
  24. package/dist/utils/mode/impl/worker_threads/agent.js +78 -85
  25. package/dist/utils/mode/impl/worker_threads/app.d.ts +28 -24
  26. package/dist/utils/mode/impl/worker_threads/app.js +128 -137
  27. package/dist/utils/options.d.ts +79 -76
  28. package/dist/utils/options.js +55 -80
  29. package/dist/utils/terminate.js +50 -70
  30. package/dist/utils/worker_manager.d.ts +28 -24
  31. package/dist/utils/worker_manager.js +68 -74
  32. package/package.json +33 -34
  33. package/dist/error/index.d.ts +0 -2
  34. package/dist/error/index.js +0 -3
  35. package/dist/utils/terminate.d.ts +0 -6
package/dist/master.js CHANGED
@@ -1,564 +1,433 @@
1
- import os from 'node:os';
2
- import v8 from 'node:v8';
3
- import util from 'node:util';
4
- import path from 'node:path';
5
- import fs from 'node:fs';
6
- import net from 'node:net';
7
- import { debuglog } from 'node:util';
8
- import { ReadyEventEmitter } from 'get-ready';
9
- import { detectPort } from 'detect-port';
10
- import { reload } from 'cluster-reload';
11
- import { EggConsoleLogger as ConsoleLogger } from 'egg-logger';
12
- import { readJSONSync } from 'utility';
13
- import terminalLink from 'terminal-link';
1
+ import { ClusterWorkerExceptionError } from "./error/ClusterWorkerExceptionError.js";
2
+ import { Messenger } from "./utils/messenger.js";
3
+ import { AgentProcessUtils } from "./utils/mode/impl/process/agent.js";
4
+ import { AppProcessUtils } from "./utils/mode/impl/process/app.js";
5
+ import { AgentThreadUtils } from "./utils/mode/impl/worker_threads/agent.js";
6
+ import { AppThreadUtils } from "./utils/mode/impl/worker_threads/app.js";
14
7
  import { parseOptions } from "./utils/options.js";
15
8
  import { WorkerManager } from "./utils/worker_manager.js";
16
- import { Messenger } from "./utils/messenger.js";
17
- import { AgentProcessWorker, AgentProcessUtils as ProcessAgentWorker } from "./utils/mode/impl/process/agent.js";
18
- import { AppProcessWorker, AppProcessUtils as ProcessAppWorker } from "./utils/mode/impl/process/app.js";
19
- import { AgentThreadWorker, AgentThreadUtils as WorkerThreadsAgentWorker, } from "./utils/mode/impl/worker_threads/agent.js";
20
- import { AppThreadWorker, AppThreadUtils as WorkerThreadsAppWorker } from "./utils/mode/impl/worker_threads/app.js";
21
- import { ClusterWorkerExceptionError } from "./error/ClusterWorkerExceptionError.js";
22
- const debug = debuglog('egg/cluster/master');
23
- export class Master extends ReadyEventEmitter {
24
- options;
25
- isStarted = false;
26
- workerManager;
27
- messenger;
28
- isProduction;
29
- agentWorkerIndex = 0;
30
- closed = false;
31
- logger;
32
- agentWorker;
33
- appWorker;
34
- #logMethod;
35
- #realPort;
36
- #protocol;
37
- #appAddress;
38
- constructor(options) {
39
- super();
40
- this.#start(options).catch((err) => {
41
- this.ready(err);
42
- });
43
- }
44
- async #start(options) {
45
- this.options = await parseOptions(options);
46
- this.workerManager = new WorkerManager();
47
- this.messenger = new Messenger(this, this.workerManager);
48
- this.isProduction = isProduction(this.options);
49
- this.#realPort = this.options.port;
50
- this.#protocol = this.options.https ? 'https' : 'http';
51
- // app started or not
52
- this.isStarted = false;
53
- this.logger = new ConsoleLogger({
54
- level: process.env.EGG_MASTER_LOGGER_LEVEL ?? 'INFO',
55
- });
56
- this.#logMethod = 'info';
57
- if (this.options.env === 'local' || process.env.NODE_ENV === 'development') {
58
- this.#logMethod = 'debug';
59
- }
60
- // get the real framework info
61
- const frameworkPath = this.options.framework;
62
- const frameworkPkg = readJSONSync(path.join(frameworkPath, 'package.json'));
63
- this.log(`[master] =================== ${frameworkPkg.name} start 🥚🥚🥚🥚 =====================`);
64
- this.logger.info(`[master] node version ${process.version}`);
65
- /* istanbul ignore next */
66
- if ('alinode' in process) {
67
- this.logger.info(`[master] alinode version ${process.alinode}`);
68
- }
69
- this.logger.info(`[master] ${frameworkPkg.name} version ${frameworkPkg.version}`);
70
- if (this.isProduction) {
71
- this.logger.info('[master] start with options:%s%s', os.EOL, JSON.stringify(this.options, null, 2));
72
- }
73
- else {
74
- this.log('[master] start with options: %j', this.options);
75
- }
76
- this.log('[master] start with env: isProduction: %s, EGG_SERVER_ENV: %s, NODE_ENV: %s', this.isProduction, this.options.env, process.env.NODE_ENV);
77
- const startTime = Date.now();
78
- this.ready(() => {
79
- this.isStarted = true;
80
- const stickyMsg = this.options.sticky ? ' with STICKY MODE!' : '';
81
- const startedURL = terminalLink(this.#appAddress, this.#appAddress, {
82
- fallback: false,
83
- });
84
- this.logger.info('[master] %s started on %s (%sms)%s', frameworkPkg.name, startedURL, Date.now() - startTime, stickyMsg);
85
- if (this.options.debugPort) {
86
- const url = getAddress({
87
- port: this.options.debugPort,
88
- protocol: 'http',
89
- });
90
- const debugPortURL = terminalLink(url, url, { fallback: false });
91
- this.logger.info('[master] %s started debug port on %s', frameworkPkg.name, debugPortURL);
92
- }
93
- const action = 'egg-ready';
94
- this.messenger.send({
95
- action,
96
- to: 'parent',
97
- data: {
98
- port: this.#realPort,
99
- debugPort: this.options.debugPort,
100
- address: this.#appAddress,
101
- protocol: this.#protocol,
102
- },
103
- });
104
- this.messenger.send({
105
- action,
106
- to: 'app',
107
- data: this.options,
108
- });
109
- this.messenger.send({
110
- action,
111
- to: 'agent',
112
- data: this.options,
113
- });
114
- // start check agent and worker status
115
- if (this.isProduction) {
116
- this.workerManager.startCheck();
117
- }
118
- });
119
- this.on('agent-exit', this.onAgentExit.bind(this));
120
- this.on('agent-start', this.onAgentStart.bind(this));
121
- this.on('app-exit', this.onAppExit.bind(this));
122
- this.on('app-start', this.onAppStart.bind(this));
123
- this.on('reload-worker', this.onReload.bind(this));
124
- // fork app workers after agent started
125
- this.once('agent-start', this.forkAppWorkers.bind(this));
126
- // get the real port from options and app.config
127
- // app worker will send after loading
128
- this.on('realport', ({ port, protocol }) => {
129
- // this.logger.info('[master] got realport: %s, protocol: %s', port, protocol);
130
- if (port) {
131
- this.#realPort = port;
132
- }
133
- if (protocol) {
134
- this.#protocol = protocol;
135
- }
136
- });
137
- // https://nodejs.org/api/process.html#process_signal_events
138
- // https://en.wikipedia.org/wiki/Unix_signal
139
- // kill(2) Ctrl-C
140
- process.once('SIGINT', this.onSignal.bind(this, 'SIGINT'));
141
- // kill(3) Ctrl-\
142
- process.once('SIGQUIT', this.onSignal.bind(this, 'SIGQUIT'));
143
- // kill(15) default
144
- process.once('SIGTERM', this.onSignal.bind(this, 'SIGTERM'));
145
- process.once('exit', this.onExit.bind(this));
146
- // write pid to file if provided
147
- if (this.options.pidFile) {
148
- fs.mkdirSync(path.dirname(this.options.pidFile), { recursive: true });
149
- fs.writeFileSync(this.options.pidFile, process.pid.toString(), 'utf-8');
150
- }
151
- // exit when agent or worker exception
152
- this.workerManager.on('exception', (count) => {
153
- const err = new ClusterWorkerExceptionError(count.agent, count.worker);
154
- this.logger.error(err);
155
- process.exit(1);
156
- });
157
- await this.detectPorts();
158
- // set app & agent worker impl
159
- if (this.options.startMode === 'worker_threads') {
160
- this.startByWorkerThreads();
161
- }
162
- else {
163
- this.startByProcess();
164
- }
165
- this.forkAgentWorker();
166
- }
167
- startByProcess() {
168
- this.agentWorker = new ProcessAgentWorker(this.options, {
169
- log: this.log.bind(this),
170
- logger: this.logger,
171
- messenger: this.messenger,
172
- });
173
- this.appWorker = new ProcessAppWorker(this.options, {
174
- log: this.log.bind(this),
175
- logger: this.logger,
176
- messenger: this.messenger,
177
- isProduction: this.isProduction,
178
- });
179
- }
180
- startByWorkerThreads() {
181
- this.agentWorker = new WorkerThreadsAgentWorker(this.options, {
182
- log: this.log.bind(this),
183
- logger: this.logger,
184
- messenger: this.messenger,
185
- });
186
- this.appWorker = new WorkerThreadsAppWorker(this.options, {
187
- log: this.log.bind(this),
188
- logger: this.logger,
189
- messenger: this.messenger,
190
- isProduction: this.isProduction,
191
- });
192
- }
193
- async detectPorts() {
194
- // Detect cluster client port
195
- try {
196
- const clusterPort = await detectPort();
197
- this.options.clusterPort = clusterPort;
198
- this.log('[master] detected cluster port: %s', clusterPort);
199
- // If sticky mode, detect worker port
200
- if (this.options.sticky) {
201
- const stickyWorkerPort = await detectPort();
202
- this.options.stickyWorkerPort = stickyWorkerPort;
203
- }
204
- }
205
- catch (err) {
206
- this.logger.error(err);
207
- process.exit(1);
208
- }
209
- }
210
- log(msg, ...args) {
211
- debug(msg, ...args);
212
- this.logger[this.#logMethod](msg, ...args);
213
- }
214
- startMasterSocketServer(cb) {
215
- // Create the outside facing server listening on our port.
216
- net
217
- .createServer({
218
- pauseOnConnect: true,
219
- }, (connection) => {
220
- // We received a connection and need to pass it to the appropriate
221
- // worker. Get the worker for this connection's source IP and pass
222
- // it the connection.
223
- /* istanbul ignore next */
224
- if (!connection.remoteAddress) {
225
- // This will happen when a client sends an RST(which is set to 1) right
226
- // after the three-way handshake to the server.
227
- // Read https://en.wikipedia.org/wiki/TCP_reset_attack for more details.
228
- connection.destroy();
229
- }
230
- else {
231
- const worker = this.stickyWorker(connection.remoteAddress);
232
- worker.instance.send('sticky-session:connection', connection);
233
- }
234
- })
235
- .listen(this.#realPort, cb);
236
- }
237
- stickyWorker(ip) {
238
- const workerNumbers = this.options.workers;
239
- const ws = this.workerManager.listWorkerIds();
240
- let s = '';
241
- for (let i = 0; i < ip.length; i++) {
242
- if (!isNaN(parseInt(ip[i]))) {
243
- s += ip[i];
244
- }
245
- }
246
- const pid = ws[Number(s) % workerNumbers];
247
- return this.workerManager.getWorker(pid);
248
- }
249
- forkAgentWorker() {
250
- this.agentWorker.on('agent_forked', (agent) => {
251
- this.workerManager.setAgent(agent);
252
- });
253
- this.agentWorker.fork();
254
- }
255
- forkAppWorkers() {
256
- this.appWorker.on('worker_forked', (worker) => {
257
- this.workerManager.setWorker(worker);
258
- });
259
- this.appWorker.fork();
260
- }
261
- /**
262
- * close agent worker, App Worker will closed by cluster
263
- *
264
- * https://www.exratione.com/2013/05/die-child-process-die/
265
- * make sure Agent Worker exit before master exit
266
- *
267
- * @param {number} timeout - kill agent timeout
268
- * @return {Promise} -
269
- */
270
- async killAgentWorker(timeout) {
271
- await this.agentWorker.kill(timeout);
272
- }
273
- async killAppWorkers(timeout) {
274
- await this.appWorker.kill(timeout);
275
- }
276
- /**
277
- * Agent Worker exit handler
278
- * Will exit during startup, and refork during running.
279
- */
280
- onAgentExit(data) {
281
- if (this.closed)
282
- return;
283
- this.messenger.send({
284
- action: 'egg-pids',
285
- to: 'app',
286
- data: [],
287
- });
288
- const agentWorker = this.agentWorker;
289
- this.workerManager.deleteAgent();
290
- const err = new Error(util.format('[master] agent_worker#%s:%s died (code: %s, signal: %s)', agentWorker.instance.id, agentWorker.instance.workerId, data.code, data.signal));
291
- err.name = 'AgentWorkerDiedError';
292
- this.logger.error(err);
293
- // remove all listeners to avoid memory leak
294
- agentWorker.clean();
295
- if (this.isStarted) {
296
- this.log('[master] try to start a new agent_worker after 1s ...');
297
- setTimeout(() => {
298
- this.logger.info('[master] new agent_worker starting...');
299
- this.forkAgentWorker();
300
- }, 1000);
301
- this.messenger.send({
302
- action: 'agent-worker-died',
303
- to: 'parent',
304
- });
305
- }
306
- else {
307
- this.logger.error('[master] agent_worker#%s:%s start fail, exiting with code:1', agentWorker.instance.id, agentWorker.instance.workerId);
308
- process.exit(1);
309
- }
310
- }
311
- onAgentStart() {
312
- this.agentWorker.instance.status = 'started';
313
- // Send egg-ready when agent is started after launched
314
- if (this.appWorker.isAllWorkerStarted) {
315
- this.messenger.send({
316
- action: 'egg-ready',
317
- to: 'agent',
318
- data: this.options,
319
- });
320
- }
321
- this.messenger.send({
322
- action: 'egg-pids',
323
- to: 'app',
324
- data: [this.agentWorker.instance.workerId],
325
- });
326
- // should send current worker pids when agent restart
327
- if (this.isStarted) {
328
- this.messenger.send({
329
- action: 'egg-pids',
330
- to: 'agent',
331
- data: this.workerManager.getListeningWorkerIds(),
332
- });
333
- }
334
- this.messenger.send({
335
- action: 'agent-start',
336
- to: 'app',
337
- });
338
- this.logger.info('[master] agent_worker#%s:%s started (%sms)', this.agentWorker.instance.id, this.agentWorker.instance.workerId, Date.now() - this.agentWorker.startTime);
339
- }
340
- /**
341
- * App Worker exit handler
342
- */
343
- onAppExit(data) {
344
- if (this.closed)
345
- return;
346
- const worker = this.workerManager.getWorker(data.workerId);
347
- if (!worker.isDevReload) {
348
- const signal = data.signal;
349
- const message = util.format('[master] app_worker#%s:%s died (code: %s, signal: %s, suicide: %s, state: %s), current workers: %j', worker.id, worker.workerId, worker.exitCode, signal, worker.exitedAfterDisconnect, worker.state, this.workerManager.listWorkerIds());
350
- if (this.options.isDebug && signal === 'SIGKILL') {
351
- // exit if died during debug
352
- this.logger.error(message);
353
- this.logger.error('[master] worker kill by debugger, exiting...');
354
- setTimeout(() => this.close(), 10);
355
- }
356
- else {
357
- const err = new Error(message);
358
- err.name = 'AppWorkerDiedError';
359
- this.logger.error(err);
360
- }
361
- }
362
- // remove all listeners to avoid memory leak
363
- worker.clean();
364
- this.workerManager.deleteWorker(data.workerId);
365
- // send message to agent with alive workers
366
- this.messenger.send({
367
- action: 'egg-pids',
368
- to: 'agent',
369
- data: this.workerManager.getListeningWorkerIds(),
370
- });
371
- if (this.appWorker.isAllWorkerStarted) {
372
- // cfork will only refork at production mode
373
- this.messenger.send({
374
- action: 'app-worker-died',
375
- to: 'parent',
376
- });
377
- }
378
- else {
379
- // exit if died during startup
380
- this.logger.error('[master] app_worker#%s:%s start fail, exiting with code:1', worker.id, worker.workerId);
381
- process.exit(1);
382
- }
383
- }
384
- /**
385
- * after app worker
386
- */
387
- onAppStart(data) {
388
- const worker = this.workerManager.getWorker(data.workerId);
389
- debug('got app_worker#%s:%s app-start event, data: %j', worker.id, worker.workerId, data);
390
- const address = data.address;
391
- // worker should listen stickyWorkerPort when sticky mode
392
- if (this.options.sticky) {
393
- if (String(address.port) !== String(this.options.stickyWorkerPort)) {
394
- return;
395
- }
396
- // worker should listen REALPORT when not sticky mode
397
- }
398
- else if (this.options.startMode !== 'worker_threads' &&
399
- !isUnixSock(address) &&
400
- String(address.port) !== String(this.#realPort)) {
401
- return;
402
- }
403
- worker.state = 'listening';
404
- // send message to agent with alive workers
405
- this.messenger.send({
406
- action: 'egg-pids',
407
- to: 'agent',
408
- data: this.workerManager.getListeningWorkerIds(),
409
- });
410
- // send message to app with current agent worker id
411
- this.messenger.send({
412
- action: 'egg-pids',
413
- to: 'app',
414
- data: [this.agentWorker.instance.workerId],
415
- receiverWorkerId: String(worker.workerId),
416
- receiverPid: String(worker.workerId),
417
- });
418
- this.appWorker.startSuccessCount++;
419
- const remain = this.appWorker.isAllWorkerStarted ? 0 : this.options.workers - this.appWorker.startSuccessCount;
420
- this.log('[master] app_worker#%s:%s started at %s, remain %s (%sms)', worker.id, worker.workerId, address.port, remain, Date.now() - this.appWorker.startTime);
421
- // Send egg-ready when app is started after launched
422
- if (this.appWorker.isAllWorkerStarted) {
423
- this.messenger.send({
424
- action: 'egg-ready',
425
- to: 'app',
426
- data: this.options,
427
- });
428
- }
429
- // if app is started, it should enable this worker
430
- if (this.appWorker.isAllWorkerStarted) {
431
- worker.disableRefork = false;
432
- }
433
- if (this.appWorker.isAllWorkerStarted || this.appWorker.startSuccessCount < this.options.workers) {
434
- return;
435
- }
436
- this.appWorker.isAllWorkerStarted = true;
437
- // enable all workers when app started
438
- for (const worker of this.workerManager.listWorkers()) {
439
- worker.disableRefork = false;
440
- }
441
- address.protocol = this.#protocol;
442
- address.port = this.options.sticky ? this.#realPort : address.port;
443
- this.#appAddress = getAddress(address);
444
- if (this.options.sticky) {
445
- this.startMasterSocketServer((err) => {
446
- if (err) {
447
- return this.ready(err);
448
- }
449
- this.ready(true);
450
- });
451
- }
452
- else {
453
- this.ready(true);
454
- }
455
- }
456
- /**
457
- * master exit handler
458
- */
459
- onExit(code) {
460
- if (this.options.pidFile && fs.existsSync(this.options.pidFile)) {
461
- try {
462
- fs.unlinkSync(this.options.pidFile);
463
- }
464
- catch (err) {
465
- /* istanbul ignore next */
466
- this.logger.error('[master] delete pidFile %s fail with %s', this.options.pidFile, err.message);
467
- }
468
- }
469
- // istanbul can't cover here
470
- // https://github.com/gotwarlost/istanbul/issues/567
471
- const level = code === 0 ? 'info' : 'error';
472
- this.logger[level]('[master] exit with code:%s', code);
473
- }
474
- onSignal(signal) {
475
- if (this.closed)
476
- return;
477
- this.log('[master] master is killed by signal %s, closing', signal);
478
- if (this.isProduction) {
479
- // logger more info
480
- const { used_heap_size, heap_size_limit } = v8.getHeapStatistics();
481
- this.logger.info('[master] system memory: total %s, free %s', os.totalmem(), os.freemem());
482
- this.logger.info('[master] process info: heap_limit %s, heap_used %s', heap_size_limit, used_heap_size);
483
- }
484
- this.close();
485
- }
486
- /**
487
- * reload workers, for develop purpose
488
- */
489
- onReload() {
490
- this.log('[master] reload %s workers...', this.options.workers);
491
- for (const worker of this.workerManager.listWorkers()) {
492
- worker.isDevReload = true;
493
- }
494
- reload(this.options.workers);
495
- }
496
- async close() {
497
- this.closed = true;
498
- setTimeout(() => {
499
- this.log('[master] close timeout, exiting with code:2');
500
- process.exit(2);
501
- }, 15000);
502
- try {
503
- await this._doClose();
504
- this.log('[master] close done, exiting with code:0');
505
- process.exit(0);
506
- }
507
- catch (e) {
508
- this.logger.error('[master] close with error: ', e);
509
- process.exit(1);
510
- }
511
- }
512
- async _doClose() {
513
- // kill app workers
514
- // kill agent worker
515
- // exit itself
516
- const legacyTimeout = process.env.EGG_MASTER_CLOSE_TIMEOUT || '5000';
517
- const appTimeout = parseInt(process.env.EGG_APP_CLOSE_TIMEOUT || legacyTimeout);
518
- const agentTimeout = parseInt(process.env.EGG_AGENT_CLOSE_TIMEOUT || legacyTimeout);
519
- this.logger.info('[master] send kill SIGTERM to app workers, will exit with code:0 after %sms', appTimeout);
520
- this.logger.info('[master] wait %sms', appTimeout);
521
- try {
522
- await this.killAppWorkers(appTimeout);
523
- }
524
- catch (e) {
525
- this.logger.error('[master] app workers exit error: ', e);
526
- }
527
- this.logger.info('[master] send kill SIGTERM to agent worker, will exit with code:0 after %sms', agentTimeout);
528
- this.logger.info('[master] wait %sms', agentTimeout);
529
- try {
530
- await this.killAgentWorker(agentTimeout);
531
- }
532
- catch (e) {
533
- this.logger.error('[master] agent worker exit error: ', e);
534
- }
535
- }
536
- }
9
+ import fs from "node:fs";
10
+ import net from "node:net";
11
+ import os from "node:os";
12
+ import path from "node:path";
13
+ import util, { debuglog } from "node:util";
14
+ import v8 from "node:v8";
15
+ import { reload } from "cluster-reload";
16
+ import { detectPort } from "detect-port";
17
+ import { EggConsoleLogger } from "egg-logger";
18
+ import { ReadyEventEmitter } from "get-ready";
19
+ import terminalLink from "terminal-link";
20
+ import { readJSONSync } from "utility";
21
+
22
+ //#region src/master.ts
23
+ const debug = debuglog("egg/cluster/master");
24
+ var Master = class extends ReadyEventEmitter {
25
+ options;
26
+ isStarted = false;
27
+ workerManager;
28
+ messenger;
29
+ isProduction;
30
+ agentWorkerIndex = 0;
31
+ closed = false;
32
+ logger;
33
+ agentWorker;
34
+ appWorker;
35
+ #logMethod;
36
+ #realPort;
37
+ #protocol;
38
+ #appAddress;
39
+ constructor(options) {
40
+ super();
41
+ this.#start(options).catch((err) => {
42
+ this.ready(err);
43
+ });
44
+ }
45
+ async #start(options) {
46
+ this.options = await parseOptions(options);
47
+ this.workerManager = new WorkerManager();
48
+ this.messenger = new Messenger(this, this.workerManager);
49
+ this.isProduction = isProduction(this.options);
50
+ this.#realPort = this.options.port;
51
+ this.#protocol = this.options.https ? "https" : "http";
52
+ this.isStarted = false;
53
+ this.logger = new EggConsoleLogger({ level: process.env.EGG_MASTER_LOGGER_LEVEL ?? "INFO" });
54
+ this.#logMethod = "info";
55
+ if (this.options.env === "local" || process.env.NODE_ENV === "development") this.#logMethod = "debug";
56
+ const frameworkPath = this.options.framework;
57
+ const frameworkPkg = readJSONSync(path.join(frameworkPath, "package.json"));
58
+ this.log(`[master] =================== ${frameworkPkg.name} start 🥚🥚🥚🥚 =====================`);
59
+ this.logger.info(`[master] node version ${process.version}`);
60
+ /* istanbul ignore next */
61
+ if ("alinode" in process) this.logger.info(`[master] alinode version ${process.alinode}`);
62
+ this.logger.info(`[master] ${frameworkPkg.name} version ${frameworkPkg.version}`);
63
+ if (this.isProduction) this.logger.info("[master] start with options:%s%s", os.EOL, JSON.stringify(this.options, null, 2));
64
+ else this.log("[master] start with options: %j", this.options);
65
+ this.log("[master] start with env: isProduction: %s, EGG_SERVER_ENV: %s, NODE_ENV: %s", this.isProduction, this.options.env, process.env.NODE_ENV);
66
+ const startTime = Date.now();
67
+ this.ready(() => {
68
+ this.isStarted = true;
69
+ const stickyMsg = this.options.sticky ? " with STICKY MODE!" : "";
70
+ const startedURL = terminalLink(this.#appAddress, this.#appAddress, { fallback: false });
71
+ this.logger.info("[master] %s started on %s (%sms)%s", frameworkPkg.name, startedURL, Date.now() - startTime, stickyMsg);
72
+ if (this.options.debugPort) {
73
+ const url = getAddress({
74
+ port: this.options.debugPort,
75
+ protocol: "http"
76
+ });
77
+ const debugPortURL = terminalLink(url, url, { fallback: false });
78
+ this.logger.info("[master] %s started debug port on %s", frameworkPkg.name, debugPortURL);
79
+ }
80
+ const action = "egg-ready";
81
+ this.messenger.send({
82
+ action,
83
+ to: "parent",
84
+ data: {
85
+ port: this.#realPort,
86
+ debugPort: this.options.debugPort,
87
+ address: this.#appAddress,
88
+ protocol: this.#protocol
89
+ }
90
+ });
91
+ this.messenger.send({
92
+ action,
93
+ to: "app",
94
+ data: this.options
95
+ });
96
+ this.messenger.send({
97
+ action,
98
+ to: "agent",
99
+ data: this.options
100
+ });
101
+ if (this.isProduction) this.workerManager.startCheck();
102
+ });
103
+ this.on("agent-exit", this.onAgentExit.bind(this));
104
+ this.on("agent-start", this.onAgentStart.bind(this));
105
+ this.on("app-exit", this.onAppExit.bind(this));
106
+ this.on("app-start", this.onAppStart.bind(this));
107
+ this.on("reload-worker", this.onReload.bind(this));
108
+ this.once("agent-start", this.forkAppWorkers.bind(this));
109
+ this.on("realport", ({ port, protocol }) => {
110
+ if (port) this.#realPort = port;
111
+ if (protocol) this.#protocol = protocol;
112
+ });
113
+ process.once("SIGINT", this.onSignal.bind(this, "SIGINT"));
114
+ process.once("SIGQUIT", this.onSignal.bind(this, "SIGQUIT"));
115
+ process.once("SIGTERM", this.onSignal.bind(this, "SIGTERM"));
116
+ process.once("exit", this.onExit.bind(this));
117
+ if (this.options.pidFile) {
118
+ fs.mkdirSync(path.dirname(this.options.pidFile), { recursive: true });
119
+ fs.writeFileSync(this.options.pidFile, process.pid.toString(), "utf-8");
120
+ }
121
+ this.workerManager.on("exception", (count) => {
122
+ const err = new ClusterWorkerExceptionError(count.agent, count.worker);
123
+ this.logger.error(err);
124
+ process.exit(1);
125
+ });
126
+ await this.detectPorts();
127
+ if (this.options.startMode === "worker_threads") this.startByWorkerThreads();
128
+ else this.startByProcess();
129
+ this.forkAgentWorker();
130
+ }
131
+ startByProcess() {
132
+ this.agentWorker = new AgentProcessUtils(this.options, {
133
+ log: this.log.bind(this),
134
+ logger: this.logger,
135
+ messenger: this.messenger
136
+ });
137
+ this.appWorker = new AppProcessUtils(this.options, {
138
+ log: this.log.bind(this),
139
+ logger: this.logger,
140
+ messenger: this.messenger,
141
+ isProduction: this.isProduction
142
+ });
143
+ }
144
+ startByWorkerThreads() {
145
+ this.agentWorker = new AgentThreadUtils(this.options, {
146
+ log: this.log.bind(this),
147
+ logger: this.logger,
148
+ messenger: this.messenger
149
+ });
150
+ this.appWorker = new AppThreadUtils(this.options, {
151
+ log: this.log.bind(this),
152
+ logger: this.logger,
153
+ messenger: this.messenger,
154
+ isProduction: this.isProduction
155
+ });
156
+ }
157
+ async detectPorts() {
158
+ try {
159
+ const clusterPort = await detectPort();
160
+ this.options.clusterPort = clusterPort;
161
+ this.log("[master] detected cluster port: %s", clusterPort);
162
+ if (this.options.sticky) {
163
+ const stickyWorkerPort = await detectPort();
164
+ this.options.stickyWorkerPort = stickyWorkerPort;
165
+ }
166
+ } catch (err) {
167
+ this.logger.error(err);
168
+ process.exit(1);
169
+ }
170
+ }
171
+ log(msg, ...args) {
172
+ debug(msg, ...args);
173
+ this.logger[this.#logMethod](msg, ...args);
174
+ }
175
+ startMasterSocketServer(cb) {
176
+ net.createServer({ pauseOnConnect: true }, (connection) => {
177
+ /* istanbul ignore next */
178
+ if (!connection.remoteAddress) connection.destroy();
179
+ else this.stickyWorker(connection.remoteAddress).instance.send("sticky-session:connection", connection);
180
+ }).listen(this.#realPort, cb);
181
+ }
182
+ stickyWorker(ip) {
183
+ const workerNumbers = this.options.workers;
184
+ const ws = this.workerManager.listWorkerIds();
185
+ let s = "";
186
+ for (let i = 0; i < ip.length; i++) if (!isNaN(parseInt(ip[i]))) s += ip[i];
187
+ const pid = ws[Number(s) % workerNumbers];
188
+ return this.workerManager.getWorker(pid);
189
+ }
190
+ forkAgentWorker() {
191
+ this.agentWorker.on("agent_forked", (agent) => {
192
+ this.workerManager.setAgent(agent);
193
+ });
194
+ this.agentWorker.fork();
195
+ }
196
+ forkAppWorkers() {
197
+ this.appWorker.on("worker_forked", (worker) => {
198
+ this.workerManager.setWorker(worker);
199
+ });
200
+ this.appWorker.fork();
201
+ }
202
+ /**
203
+ * close agent worker, App Worker will closed by cluster
204
+ *
205
+ * https://www.exratione.com/2013/05/die-child-process-die/
206
+ * make sure Agent Worker exit before master exit
207
+ *
208
+ * @param {number} timeout - kill agent timeout
209
+ * @return {Promise} -
210
+ */
211
+ async killAgentWorker(timeout) {
212
+ await this.agentWorker.kill(timeout);
213
+ }
214
+ async killAppWorkers(timeout) {
215
+ await this.appWorker.kill(timeout);
216
+ }
217
+ /**
218
+ * Agent Worker exit handler
219
+ * Will exit during startup, and refork during running.
220
+ */
221
+ onAgentExit(data) {
222
+ if (this.closed) return;
223
+ this.messenger.send({
224
+ action: "egg-pids",
225
+ to: "app",
226
+ data: []
227
+ });
228
+ const agentWorker = this.agentWorker;
229
+ this.workerManager.deleteAgent();
230
+ const err = new Error(util.format("[master] agent_worker#%s:%s died (code: %s, signal: %s)", agentWorker.instance.id, agentWorker.instance.workerId, data.code, data.signal));
231
+ err.name = "AgentWorkerDiedError";
232
+ this.logger.error(err);
233
+ agentWorker.clean();
234
+ if (this.isStarted) {
235
+ this.log("[master] try to start a new agent_worker after 1s ...");
236
+ setTimeout(() => {
237
+ this.logger.info("[master] new agent_worker starting...");
238
+ this.forkAgentWorker();
239
+ }, 1e3);
240
+ this.messenger.send({
241
+ action: "agent-worker-died",
242
+ to: "parent"
243
+ });
244
+ } else {
245
+ this.logger.error("[master] agent_worker#%s:%s start fail, exiting with code:1", agentWorker.instance.id, agentWorker.instance.workerId);
246
+ process.exit(1);
247
+ }
248
+ }
249
+ onAgentStart() {
250
+ this.agentWorker.instance.status = "started";
251
+ if (this.appWorker.isAllWorkerStarted) this.messenger.send({
252
+ action: "egg-ready",
253
+ to: "agent",
254
+ data: this.options
255
+ });
256
+ this.messenger.send({
257
+ action: "egg-pids",
258
+ to: "app",
259
+ data: [this.agentWorker.instance.workerId]
260
+ });
261
+ if (this.isStarted) this.messenger.send({
262
+ action: "egg-pids",
263
+ to: "agent",
264
+ data: this.workerManager.getListeningWorkerIds()
265
+ });
266
+ this.messenger.send({
267
+ action: "agent-start",
268
+ to: "app"
269
+ });
270
+ this.logger.info("[master] agent_worker#%s:%s started (%sms)", this.agentWorker.instance.id, this.agentWorker.instance.workerId, Date.now() - this.agentWorker.startTime);
271
+ }
272
+ /**
273
+ * App Worker exit handler
274
+ */
275
+ onAppExit(data) {
276
+ if (this.closed) return;
277
+ const worker = this.workerManager.getWorker(data.workerId);
278
+ if (!worker.isDevReload) {
279
+ const signal = data.signal;
280
+ const message = util.format("[master] app_worker#%s:%s died (code: %s, signal: %s, suicide: %s, state: %s), current workers: %j", worker.id, worker.workerId, worker.exitCode, signal, worker.exitedAfterDisconnect, worker.state, this.workerManager.listWorkerIds());
281
+ if (this.options.isDebug && signal === "SIGKILL") {
282
+ this.logger.error(message);
283
+ this.logger.error("[master] worker kill by debugger, exiting...");
284
+ setTimeout(() => this.close(), 10);
285
+ } else {
286
+ const err = new Error(message);
287
+ err.name = "AppWorkerDiedError";
288
+ this.logger.error(err);
289
+ }
290
+ }
291
+ worker.clean();
292
+ this.workerManager.deleteWorker(data.workerId);
293
+ this.messenger.send({
294
+ action: "egg-pids",
295
+ to: "agent",
296
+ data: this.workerManager.getListeningWorkerIds()
297
+ });
298
+ if (this.appWorker.isAllWorkerStarted) this.messenger.send({
299
+ action: "app-worker-died",
300
+ to: "parent"
301
+ });
302
+ else {
303
+ this.logger.error("[master] app_worker#%s:%s start fail, exiting with code:1", worker.id, worker.workerId);
304
+ process.exit(1);
305
+ }
306
+ }
307
+ /**
308
+ * after app worker
309
+ */
310
+ onAppStart(data) {
311
+ const worker = this.workerManager.getWorker(data.workerId);
312
+ debug("got app_worker#%s:%s app-start event, data: %j", worker.id, worker.workerId, data);
313
+ const address = data.address;
314
+ if (this.options.sticky) {
315
+ if (String(address.port) !== String(this.options.stickyWorkerPort)) return;
316
+ } else if (this.options.startMode !== "worker_threads" && !isUnixSock(address) && String(address.port) !== String(this.#realPort)) return;
317
+ worker.state = "listening";
318
+ this.messenger.send({
319
+ action: "egg-pids",
320
+ to: "agent",
321
+ data: this.workerManager.getListeningWorkerIds()
322
+ });
323
+ this.messenger.send({
324
+ action: "egg-pids",
325
+ to: "app",
326
+ data: [this.agentWorker.instance.workerId],
327
+ receiverWorkerId: String(worker.workerId),
328
+ receiverPid: String(worker.workerId)
329
+ });
330
+ this.appWorker.startSuccessCount++;
331
+ const remain = this.appWorker.isAllWorkerStarted ? 0 : this.options.workers - this.appWorker.startSuccessCount;
332
+ this.log("[master] app_worker#%s:%s started at %s, remain %s (%sms)", worker.id, worker.workerId, address.port, remain, Date.now() - this.appWorker.startTime);
333
+ if (this.appWorker.isAllWorkerStarted) this.messenger.send({
334
+ action: "egg-ready",
335
+ to: "app",
336
+ data: this.options
337
+ });
338
+ if (this.appWorker.isAllWorkerStarted) worker.disableRefork = false;
339
+ if (this.appWorker.isAllWorkerStarted || this.appWorker.startSuccessCount < this.options.workers) return;
340
+ this.appWorker.isAllWorkerStarted = true;
341
+ for (const worker$1 of this.workerManager.listWorkers()) worker$1.disableRefork = false;
342
+ address.protocol = this.#protocol;
343
+ address.port = this.options.sticky ? this.#realPort : address.port;
344
+ this.#appAddress = getAddress(address);
345
+ if (this.options.sticky) this.startMasterSocketServer((err) => {
346
+ if (err) return this.ready(err);
347
+ this.ready(true);
348
+ });
349
+ else this.ready(true);
350
+ }
351
+ /**
352
+ * master exit handler
353
+ */
354
+ onExit(code) {
355
+ if (this.options.pidFile && fs.existsSync(this.options.pidFile)) try {
356
+ fs.unlinkSync(this.options.pidFile);
357
+ } catch (err) {
358
+ /* istanbul ignore next */
359
+ this.logger.error("[master] delete pidFile %s fail with %s", this.options.pidFile, err.message);
360
+ }
361
+ const level = code === 0 ? "info" : "error";
362
+ this.logger[level]("[master] exit with code:%s", code);
363
+ }
364
+ onSignal(signal) {
365
+ if (this.closed) return;
366
+ this.log("[master] master is killed by signal %s, closing", signal);
367
+ if (this.isProduction) {
368
+ const { used_heap_size, heap_size_limit } = v8.getHeapStatistics();
369
+ this.logger.info("[master] system memory: total %s, free %s", os.totalmem(), os.freemem());
370
+ this.logger.info("[master] process info: heap_limit %s, heap_used %s", heap_size_limit, used_heap_size);
371
+ }
372
+ this.close();
373
+ }
374
+ /**
375
+ * reload workers, for develop purpose
376
+ */
377
+ onReload() {
378
+ this.log("[master] reload %s workers...", this.options.workers);
379
+ for (const worker of this.workerManager.listWorkers()) worker.isDevReload = true;
380
+ reload(this.options.workers);
381
+ }
382
+ async close() {
383
+ this.closed = true;
384
+ setTimeout(() => {
385
+ this.log("[master] close timeout, exiting with code:2");
386
+ process.exit(2);
387
+ }, 15e3);
388
+ try {
389
+ await this._doClose();
390
+ this.log("[master] close done, exiting with code:0");
391
+ process.exit(0);
392
+ } catch (e) {
393
+ this.logger.error("[master] close with error: ", e);
394
+ process.exit(1);
395
+ }
396
+ }
397
+ async _doClose() {
398
+ const legacyTimeout = process.env.EGG_MASTER_CLOSE_TIMEOUT || "5000";
399
+ const appTimeout = parseInt(process.env.EGG_APP_CLOSE_TIMEOUT || legacyTimeout);
400
+ const agentTimeout = parseInt(process.env.EGG_AGENT_CLOSE_TIMEOUT || legacyTimeout);
401
+ this.logger.info("[master] send kill SIGTERM to app workers, will exit with code:0 after %sms", appTimeout);
402
+ this.logger.info("[master] wait %sms", appTimeout);
403
+ try {
404
+ await this.killAppWorkers(appTimeout);
405
+ } catch (e) {
406
+ this.logger.error("[master] app workers exit error: ", e);
407
+ }
408
+ this.logger.info("[master] send kill SIGTERM to agent worker, will exit with code:0 after %sms", agentTimeout);
409
+ this.logger.info("[master] wait %sms", agentTimeout);
410
+ try {
411
+ await this.killAgentWorker(agentTimeout);
412
+ } catch (e) {
413
+ this.logger.error("[master] agent worker exit error: ", e);
414
+ }
415
+ }
416
+ };
537
417
  function isProduction(options) {
538
- if (options.env) {
539
- return options.env !== 'local' && options.env !== 'unittest';
540
- }
541
- return process.env.NODE_ENV === 'production';
418
+ if (options.env) return options.env !== "local" && options.env !== "unittest";
419
+ return process.env.NODE_ENV === "production";
542
420
  }
543
421
  function getAddress({ addressType, address, port, protocol }) {
544
- // unix sock
545
- // https://nodejs.org/api/cluster.html#cluster_event_listening_1
546
- if (addressType === -1) {
547
- return address;
548
- }
549
- // {"address":"::","family":"IPv6","port":17001}
550
- if (address === '::') {
551
- address = '';
552
- }
553
- if (!address && process.env.HOST && process.env.HOST !== '0.0.0.0') {
554
- address = process.env.HOST;
555
- }
556
- if (!address) {
557
- address = '127.0.0.1';
558
- }
559
- return `${protocol}://${address}:${port}`;
422
+ if (addressType === -1) return address;
423
+ if (address === "::") address = "";
424
+ if (!address && process.env.HOST && process.env.HOST !== "0.0.0.0") address = process.env.HOST;
425
+ if (!address) address = "127.0.0.1";
426
+ return `${protocol}://${address}:${port}`;
560
427
  }
561
428
  function isUnixSock(address) {
562
- return address.addressType === -1;
429
+ return address.addressType === -1;
563
430
  }
564
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFzdGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL21hc3Rlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsTUFBTSxTQUFTLENBQUM7QUFDekIsT0FBTyxFQUFFLE1BQU0sU0FBUyxDQUFDO0FBQ3pCLE9BQU8sSUFBSSxNQUFNLFdBQVcsQ0FBQztBQUM3QixPQUFPLElBQUksTUFBTSxXQUFXLENBQUM7QUFDN0IsT0FBTyxFQUFFLE1BQU0sU0FBUyxDQUFDO0FBQ3pCLE9BQU8sR0FBRyxNQUFNLFVBQVUsQ0FBQztBQUMzQixPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0sV0FBVyxDQUFDO0FBRXJDLE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxNQUFNLFdBQVcsQ0FBQztBQUM5QyxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBQ3pDLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUN4QyxPQUFPLEVBQUUsZ0JBQWdCLElBQUksYUFBYSxFQUFFLE1BQU0sWUFBWSxDQUFDO0FBQy9ELE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxTQUFTLENBQUM7QUFDdkMsT0FBTyxZQUFZLE1BQU0sZUFBZSxDQUFDO0FBRXpDLE9BQU8sRUFBRSxZQUFZLEVBQWtELE1BQU0sb0JBQW9CLENBQUM7QUFDbEcsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLDJCQUEyQixDQUFDO0FBQzFELE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQztBQUNqRCxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsaUJBQWlCLElBQUksa0JBQWtCLEVBQUUsTUFBTSxvQ0FBb0MsQ0FBQztBQUNqSCxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsZUFBZSxJQUFJLGdCQUFnQixFQUFFLE1BQU0sa0NBQWtDLENBQUM7QUFDekcsT0FBTyxFQUNMLGlCQUFpQixFQUNqQixnQkFBZ0IsSUFBSSx3QkFBd0IsR0FDN0Ms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
431
+
432
+ //#endregion
433
+ export { Master };