@eggjs/cluster 3.0.1 → 3.1.0-beta.11

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 (127) hide show
  1. package/README.md +17 -21
  2. package/dist/agent_worker.d.ts +1 -0
  3. package/dist/agent_worker.js +55 -0
  4. package/dist/app_worker.d.ts +1 -0
  5. package/dist/app_worker.js +131 -0
  6. package/dist/dirname.js +11 -0
  7. package/dist/error/ClusterAgentWorkerError.d.ts +13 -0
  8. package/dist/error/ClusterAgentWorkerError.js +22 -0
  9. package/dist/error/ClusterWorkerExceptionError.d.ts +10 -0
  10. package/dist/error/ClusterWorkerExceptionError.js +17 -0
  11. package/dist/index.d.ts +22 -0
  12. package/dist/index.js +24 -0
  13. package/dist/master.d.ts +96 -0
  14. package/dist/master.js +426 -0
  15. package/dist/utils/messenger.d.ts +96 -0
  16. package/dist/utils/messenger.js +144 -0
  17. package/dist/utils/mode/base/agent.d.ts +45 -0
  18. package/dist/utils/mode/base/agent.js +63 -0
  19. package/dist/utils/mode/base/app.d.ts +56 -0
  20. package/dist/utils/mode/base/app.js +77 -0
  21. package/dist/utils/mode/impl/process/agent.d.ts +22 -0
  22. package/dist/utils/mode/impl/process/agent.js +93 -0
  23. package/dist/utils/mode/impl/process/app.d.ts +12 -0
  24. package/dist/utils/mode/impl/process/app.js +117 -0
  25. package/dist/utils/mode/impl/worker_threads/agent.d.ts +22 -0
  26. package/dist/utils/mode/impl/worker_threads/agent.js +79 -0
  27. package/dist/utils/mode/impl/worker_threads/app.d.ts +13 -0
  28. package/dist/utils/mode/impl/worker_threads/app.js +128 -0
  29. package/dist/utils/options.d.ts +83 -0
  30. package/dist/utils/options.js +56 -0
  31. package/dist/utils/terminate.js +62 -0
  32. package/dist/utils/worker_manager.d.ts +32 -0
  33. package/dist/utils/worker_manager.js +68 -0
  34. package/package.json +41 -63
  35. package/dist/commonjs/agent_worker.d.ts +0 -1
  36. package/dist/commonjs/agent_worker.js +0 -69
  37. package/dist/commonjs/app_worker.d.ts +0 -1
  38. package/dist/commonjs/app_worker.js +0 -173
  39. package/dist/commonjs/dirname.d.ts +0 -1
  40. package/dist/commonjs/dirname.js +0 -17
  41. package/dist/commonjs/error/ClusterAgentWorkerError.d.ts +0 -10
  42. package/dist/commonjs/error/ClusterAgentWorkerError.js +0 -23
  43. package/dist/commonjs/error/ClusterWorkerExceptionError.d.ts +0 -7
  44. package/dist/commonjs/error/ClusterWorkerExceptionError.js +0 -18
  45. package/dist/commonjs/error/index.d.ts +0 -2
  46. package/dist/commonjs/error/index.js +0 -19
  47. package/dist/commonjs/index.d.ts +0 -17
  48. package/dist/commonjs/index.js +0 -37
  49. package/dist/commonjs/master.d.ts +0 -90
  50. package/dist/commonjs/master.js +0 -560
  51. package/dist/commonjs/package.json +0 -3
  52. package/dist/commonjs/utils/messenger.d.ts +0 -92
  53. package/dist/commonjs/utils/messenger.js +0 -186
  54. package/dist/commonjs/utils/mode/base/agent.d.ts +0 -38
  55. package/dist/commonjs/utils/mode/base/agent.js +0 -68
  56. package/dist/commonjs/utils/mode/base/app.d.ts +0 -48
  57. package/dist/commonjs/utils/mode/base/app.js +0 -83
  58. package/dist/commonjs/utils/mode/impl/process/agent.d.ts +0 -18
  59. package/dist/commonjs/utils/mode/impl/process/agent.js +0 -108
  60. package/dist/commonjs/utils/mode/impl/process/app.d.ts +0 -21
  61. package/dist/commonjs/utils/mode/impl/process/app.js +0 -127
  62. package/dist/commonjs/utils/mode/impl/worker_threads/agent.d.ts +0 -18
  63. package/dist/commonjs/utils/mode/impl/worker_threads/agent.js +0 -91
  64. package/dist/commonjs/utils/mode/impl/worker_threads/app.d.ts +0 -26
  65. package/dist/commonjs/utils/mode/impl/worker_threads/app.js +0 -142
  66. package/dist/commonjs/utils/options.d.ts +0 -80
  67. package/dist/commonjs/utils/options.js +0 -83
  68. package/dist/commonjs/utils/terminate.d.ts +0 -6
  69. package/dist/commonjs/utils/terminate.js +0 -89
  70. package/dist/commonjs/utils/worker_manager.d.ts +0 -25
  71. package/dist/commonjs/utils/worker_manager.js +0 -76
  72. package/dist/esm/agent_worker.d.ts +0 -1
  73. package/dist/esm/agent_worker.js +0 -67
  74. package/dist/esm/app_worker.d.ts +0 -1
  75. package/dist/esm/app_worker.js +0 -168
  76. package/dist/esm/dirname.d.ts +0 -1
  77. package/dist/esm/dirname.js +0 -11
  78. package/dist/esm/error/ClusterAgentWorkerError.d.ts +0 -10
  79. package/dist/esm/error/ClusterAgentWorkerError.js +0 -19
  80. package/dist/esm/error/ClusterWorkerExceptionError.d.ts +0 -7
  81. package/dist/esm/error/ClusterWorkerExceptionError.js +0 -14
  82. package/dist/esm/error/index.d.ts +0 -2
  83. package/dist/esm/error/index.js +0 -3
  84. package/dist/esm/index.d.ts +0 -17
  85. package/dist/esm/index.js +0 -19
  86. package/dist/esm/master.d.ts +0 -90
  87. package/dist/esm/master.js +0 -553
  88. package/dist/esm/package.json +0 -3
  89. package/dist/esm/utils/messenger.d.ts +0 -92
  90. package/dist/esm/utils/messenger.js +0 -179
  91. package/dist/esm/utils/mode/base/agent.d.ts +0 -38
  92. package/dist/esm/utils/mode/base/agent.js +0 -60
  93. package/dist/esm/utils/mode/base/app.d.ts +0 -48
  94. package/dist/esm/utils/mode/base/app.js +0 -75
  95. package/dist/esm/utils/mode/impl/process/agent.d.ts +0 -18
  96. package/dist/esm/utils/mode/impl/process/agent.js +0 -103
  97. package/dist/esm/utils/mode/impl/process/app.d.ts +0 -21
  98. package/dist/esm/utils/mode/impl/process/app.js +0 -119
  99. package/dist/esm/utils/mode/impl/worker_threads/agent.d.ts +0 -18
  100. package/dist/esm/utils/mode/impl/worker_threads/agent.js +0 -83
  101. package/dist/esm/utils/mode/impl/worker_threads/app.d.ts +0 -26
  102. package/dist/esm/utils/mode/impl/worker_threads/app.js +0 -137
  103. package/dist/esm/utils/options.d.ts +0 -80
  104. package/dist/esm/utils/options.js +0 -77
  105. package/dist/esm/utils/terminate.d.ts +0 -6
  106. package/dist/esm/utils/terminate.js +0 -86
  107. package/dist/esm/utils/worker_manager.d.ts +0 -25
  108. package/dist/esm/utils/worker_manager.js +0 -72
  109. package/dist/package.json +0 -4
  110. package/src/agent_worker.ts +0 -80
  111. package/src/app_worker.ts +0 -196
  112. package/src/dirname.ts +0 -11
  113. package/src/error/ClusterAgentWorkerError.ts +0 -19
  114. package/src/error/ClusterWorkerExceptionError.ts +0 -17
  115. package/src/error/index.ts +0 -2
  116. package/src/index.ts +0 -26
  117. package/src/master.ts +0 -658
  118. package/src/utils/messenger.ts +0 -207
  119. package/src/utils/mode/base/agent.ts +0 -90
  120. package/src/utils/mode/base/app.ts +0 -119
  121. package/src/utils/mode/impl/process/agent.ts +0 -119
  122. package/src/utils/mode/impl/process/app.ts +0 -140
  123. package/src/utils/mode/impl/worker_threads/agent.ts +0 -99
  124. package/src/utils/mode/impl/worker_threads/app.ts +0 -164
  125. package/src/utils/options.ts +0 -171
  126. package/src/utils/terminate.ts +0 -97
  127. package/src/utils/worker_manager.ts +0 -87
@@ -1,99 +0,0 @@
1
- import workerThreads, { type Worker } from 'node:worker_threads';
2
- import { type Options as gracefulExitOptions } from 'graceful-process';
3
- import { BaseAgentUtils, BaseAgentWorker } from '../../base/agent.js';
4
- import type { MessageBody } from '../../../messenger.js';
5
- import { ClusterAgentWorkerError } from '../../../../error/ClusterAgentWorkerError.js';
6
-
7
- export class AgentThreadWorker extends BaseAgentWorker<Worker> {
8
- get workerId() {
9
- return this.instance.threadId;
10
- }
11
-
12
- send(message: MessageBody) {
13
- this.instance.postMessage(message);
14
- }
15
-
16
- static send(message: MessageBody) {
17
- message.senderWorkerId = String(workerThreads.threadId);
18
- workerThreads.parentPort!.postMessage(message);
19
- }
20
-
21
- static kill() {
22
- // in worker_threads, process.exit
23
- // does not stop the whole program, just the single thread
24
- process.exit(1);
25
- }
26
-
27
- static gracefulExit(options: gracefulExitOptions) {
28
- const { beforeExit } = options;
29
- process.on('exit', async code => {
30
- if (typeof beforeExit === 'function') {
31
- await beforeExit();
32
- }
33
- process.exit(code);
34
- });
35
- }
36
- }
37
-
38
- export class AgentThreadUtils extends BaseAgentUtils {
39
- #worker: Worker;
40
- #id = 0;
41
- instance: AgentThreadWorker;
42
-
43
- fork() {
44
- this.startTime = Date.now();
45
-
46
- // start agent worker
47
- const argv = [ JSON.stringify(this.options) ];
48
- const agentPath = this.getAgentWorkerFile();
49
- const worker = this.#worker = new workerThreads.Worker(agentPath, { argv });
50
-
51
- // wrap agent worker
52
- const agentWorker = this.instance = new AgentThreadWorker(worker);
53
- this.emit('agent_forked', agentWorker);
54
- agentWorker.status = 'starting';
55
- agentWorker.id = ++this.#id;
56
- this.log('[master] agent_worker#%s:%s start with worker_threads',
57
- agentWorker.id, agentWorker.workerId);
58
-
59
- worker.on('message', msg => {
60
- if (typeof msg === 'string') {
61
- msg = {
62
- action: msg,
63
- data: msg,
64
- };
65
- }
66
- msg.from = 'agent';
67
- this.messenger.send(msg);
68
- });
69
-
70
- worker.on('error', err => {
71
- this.logger.error(new ClusterAgentWorkerError(agentWorker.id, agentWorker.workerId, agentWorker.status, err));
72
- });
73
-
74
- // agent exit message
75
- worker.once('exit', (code: number, signal: string) => {
76
- this.messenger.send({
77
- action: 'agent-exit',
78
- data: {
79
- code,
80
- signal,
81
- },
82
- to: 'master',
83
- from: 'agent',
84
- });
85
- });
86
- }
87
-
88
- clean() {
89
- this.#worker.removeAllListeners();
90
- }
91
-
92
- async kill() {
93
- if (this.#worker) {
94
- this.log(`[master] kill agent worker#${this.#id} (worker_threads) by worker.terminate()`);
95
- this.clean();
96
- await this.#worker.terminate();
97
- }
98
- }
99
- }
@@ -1,164 +0,0 @@
1
- import { setTimeout as sleep } from 'node:timers/promises';
2
- import { Worker as ThreadWorker, threadId, parentPort, type WorkerOptions } from 'node:worker_threads';
3
- import type { Options as gracefulExitOptions } from 'graceful-process';
4
- import { BaseAppWorker, BaseAppUtils } from '../../base/app.js';
5
- import type { MessageBody } from '../../../messenger.js';
6
-
7
- export class AppThreadWorker extends BaseAppWorker<ThreadWorker> {
8
- #state = 'none';
9
- #id: number;
10
-
11
- constructor(instance: ThreadWorker, id: number) {
12
- super(instance);
13
- this.#id = id;
14
- }
15
-
16
- get id() {
17
- return this.#id;
18
- }
19
-
20
- get workerId() {
21
- return this.instance.threadId;
22
- }
23
-
24
- get state() {
25
- return this.#state;
26
- }
27
-
28
- set state(val) {
29
- this.#state = val;
30
- }
31
-
32
- get exitedAfterDisconnect() {
33
- return true;
34
- }
35
-
36
- get exitCode() {
37
- return 0;
38
- // return this.instance.exitCode;
39
- }
40
-
41
- send(message: MessageBody) {
42
- this.instance.postMessage(message);
43
- }
44
-
45
- clean() {
46
- this.instance.removeAllListeners();
47
- }
48
-
49
- // static methods use on src/app_worker.ts
50
-
51
- static get workerId() {
52
- return threadId;
53
- }
54
-
55
- static on(event: string, listener: (...args: any[]) => void) {
56
- parentPort!.on(event, listener);
57
- }
58
-
59
- static send(message: MessageBody) {
60
- message.senderWorkerId = String(threadId);
61
- parentPort!.postMessage(message);
62
- }
63
-
64
- static kill() {
65
- process.exit(1);
66
- }
67
-
68
- static gracefulExit(options: gracefulExitOptions) {
69
- process.on('exit', async code => {
70
- if (typeof options.beforeExit === 'function') {
71
- await options.beforeExit();
72
- }
73
- process.exit(code);
74
- });
75
- }
76
- }
77
-
78
- export class AppThreadUtils extends BaseAppUtils {
79
- #workers: ThreadWorker[] = [];
80
-
81
- #forkSingle(appPath: string, options: WorkerOptions, id: number) {
82
- // start app worker
83
- const worker = new ThreadWorker(appPath, options);
84
- this.#workers.push(worker);
85
-
86
- // wrap app worker
87
- const appWorker = new AppThreadWorker(worker, id);
88
- this.emit('worker_forked', appWorker);
89
- appWorker.disableRefork = true;
90
- worker.on('message', (msg: MessageBody) => {
91
- if (typeof msg === 'string') {
92
- msg = {
93
- action: msg,
94
- data: msg,
95
- };
96
- }
97
- msg.from = 'app';
98
- this.messenger.send(msg);
99
- });
100
- this.log('[master] app_worker#%s (tid:%s) start', appWorker.id, appWorker.workerId);
101
-
102
- // send debug message, due to `brk` scene, send here instead of app_worker.js
103
- let debugPort = process.debugPort;
104
- if (this.options.isDebug) {
105
- debugPort++;
106
- this.messenger.send({
107
- to: 'parent',
108
- from: 'app',
109
- action: 'debug',
110
- data: {
111
- debugPort,
112
- pid: appWorker.workerId,
113
- workerId: appWorker.workerId,
114
- },
115
- });
116
- }
117
-
118
- // handle worker exit
119
- worker.on('exit', async code => {
120
- appWorker.state = 'dead';
121
- this.messenger.send({
122
- action: 'app-exit',
123
- data: {
124
- workerId: appWorker.workerId,
125
- code,
126
- },
127
- to: 'master',
128
- from: 'app',
129
- });
130
-
131
- // refork app worker
132
- await sleep(1000);
133
- this.#forkSingle(appPath, options, id);
134
- });
135
- }
136
-
137
- fork() {
138
- this.startTime = Date.now();
139
- this.startSuccessCount = 0;
140
-
141
- const ports = this.options.ports ?? [];
142
- if (!ports.length) {
143
- ports.push(this.options.port!);
144
- }
145
- this.options.workers = ports.length;
146
- let i = 0;
147
- do {
148
- const options = Object.assign({}, this.options, { port: ports[i] });
149
- const argv = [ JSON.stringify(options) ];
150
- this.#forkSingle(this.getAppWorkerFile(), { argv }, ++i);
151
- } while (i < ports.length);
152
-
153
- return this;
154
- }
155
-
156
- async kill() {
157
- for (const worker of this.#workers) {
158
- const id = Reflect.get(worker, 'id');
159
- this.log(`[master] kill app worker#${id} (worker_threads) by worker.terminate()`);
160
- worker.removeAllListeners();
161
- worker.terminate();
162
- }
163
- }
164
- }
@@ -1,171 +0,0 @@
1
- import os from 'node:os';
2
- import fs from 'node:fs';
3
- import path from 'node:path';
4
- import assert from 'node:assert';
5
- import { SecureContextOptions } from 'node:tls';
6
- import { getFrameworkPath, importModule } from '@eggjs/utils';
7
-
8
- export interface ClusterHTTPSSecureOptions {
9
- key: SecureContextOptions['key'];
10
- cert: SecureContextOptions['cert'];
11
- ca?: SecureContextOptions['ca'];
12
- passphrase?: SecureContextOptions['passphrase'];
13
- }
14
-
15
- export type ClusterStartMode = 'process' | 'worker_threads';
16
-
17
- /** Cluster start options */
18
- export interface ClusterOptions {
19
- /**
20
- * specify framework that can be absolute path or npm package
21
- */
22
- framework?: string;
23
- /**
24
- * @deprecated please use framework instead
25
- */
26
- customEgg?: string;
27
- /** directory of application, default to `process.cwd()` */
28
- baseDir?: string;
29
- /**
30
- * numbers of app workers, default to `os.cpus().length`
31
- */
32
- workers?: number | string;
33
- /**
34
- * listening port, default to `7001`(http) or `8443`(https)
35
- */
36
- port?: number | string | null;
37
- /**
38
- * listening a debug port on http protocol
39
- */
40
- debugPort?: number;
41
- /**
42
- * https options, { key, cert, ca }, full path
43
- */
44
- https?: ClusterHTTPSSecureOptions | boolean;
45
- /**
46
- * @deprecated please use `options.https.key` instead
47
- */
48
- key?: ClusterHTTPSSecureOptions['key'];
49
- /**
50
- * @deprecated please use `options.https.cert` instead
51
- */
52
- cert?: ClusterHTTPSSecureOptions['cert'];
53
- /**
54
- * will inject into worker/agent process
55
- */
56
- require?: string | string[];
57
- /**
58
- * will save master pid to this file
59
- */
60
- pidFile?: string;
61
- /**
62
- * custom env, default is `process.env.EGG_SERVER_ENV`
63
- */
64
- env?: string;
65
- /**
66
- * default is `'process'`, use `'worker_threads'` to start the app & agent worker by worker_threads
67
- */
68
- startMode?: ClusterStartMode;
69
- /**
70
- * startup port of each app worker, such as: `[7001, 7002, 7003]`, only effects when the startMode is `'worker_threads'`
71
- */
72
- ports?: number[];
73
- /**
74
- * sticky mode server
75
- */
76
- sticky?: boolean;
77
- /** customized plugins, for unittest */
78
- plugins?: object;
79
- isDebug?: boolean;
80
- }
81
-
82
- export interface ParsedClusterOptions extends ClusterOptions {
83
- port?: number;
84
- baseDir: string;
85
- workers: number;
86
- framework: string;
87
- startMode: ClusterStartMode;
88
- }
89
-
90
- export async function parseOptions(options?: ClusterOptions) {
91
- options = {
92
- baseDir: process.cwd(),
93
- port: options?.https ? 8443 : undefined,
94
- startMode: 'process',
95
- // ports: [],
96
- env: process.env.EGG_SERVER_ENV,
97
- ...options,
98
- };
99
-
100
- const pkgPath = path.join(options.baseDir!, 'package.json');
101
- assert(fs.existsSync(pkgPath), `${pkgPath} should exist`);
102
-
103
- options.framework = getFrameworkPath({
104
- baseDir: options.baseDir!,
105
- // compatible customEgg only when call startCluster directly without framework
106
- framework: options.framework ?? options.customEgg,
107
- });
108
-
109
- const egg = await importModule(options.framework, {
110
- paths: [ options.baseDir! ],
111
- });
112
- assert(egg.Application, `should define Application in ${options.framework}`);
113
- assert(egg.Agent, `should define Agent in ${options.framework}`);
114
-
115
- if (options.https === true) {
116
- // Keep compatible options.key, options.cert
117
- console.warn('[@eggjs/cluster:deprecated] [master] Please use `https: { key, cert, ca }` instead of `https: true`');
118
- options.https = {
119
- key: options.key,
120
- cert: options.cert,
121
- };
122
- }
123
-
124
- // https
125
- if (options.https) {
126
- assert(options.https.key, 'options.https.key should exists');
127
- if (typeof options.https.key === 'string') {
128
- assert(fs.existsSync(options.https.key), 'options.https.key file should exists');
129
- }
130
- assert(options.https.cert, 'options.https.cert should exists');
131
- if (typeof options.https.cert === 'string') {
132
- assert(fs.existsSync(options.https.cert), 'options.https.cert file should exists');
133
- }
134
- if (typeof options.https.ca === 'string') {
135
- assert(fs.existsSync(options.https.ca), 'options.https.ca file should exists');
136
- }
137
- }
138
-
139
- if (options.port && typeof options.port === 'string') {
140
- options.port = parseInt(options.port);
141
- }
142
- if (options.port === null) {
143
- options.port = undefined;
144
- }
145
-
146
- if (options.workers && typeof options.workers === 'string') {
147
- options.workers = parseInt(options.workers);
148
- }
149
- if (!options.workers) {
150
- options.workers = os.cpus().length;
151
- }
152
-
153
- if (options.require) {
154
- if (typeof options.require === 'string') {
155
- options.require = [ options.require ];
156
- }
157
- }
158
-
159
- // don't print deprecated message in production env.
160
- // it will print to stderr.
161
- if (process.env.NODE_ENV === 'production') {
162
- process.env.NO_DEPRECATION = '*';
163
- }
164
-
165
- const isDebug = process.execArgv.some(argv => argv.includes('--debug') || argv.includes('--inspect'));
166
- if (isDebug) {
167
- options.isDebug = isDebug;
168
- }
169
-
170
- return options as ParsedClusterOptions;
171
- }
@@ -1,97 +0,0 @@
1
- import { debuglog } from 'node:util';
2
- import { setTimeout as sleep } from 'node:timers/promises';
3
- import { once } from 'node:events';
4
- import { ChildProcess } from 'node:child_process';
5
- import { pstree } from '@fengmk2/ps-tree';
6
-
7
- const debug = debuglog('@eggjs/cluster/utils/terminate');
8
-
9
- interface SubProcess extends ChildProcess {
10
- process?: ChildProcess;
11
- }
12
-
13
- export async function terminate(subProcess: SubProcess, timeout: number) {
14
- const pid = subProcess.process?.pid ?? subProcess.pid;
15
- const childPids = await getChildPids(pid!);
16
- await Promise.all([
17
- killProcess(subProcess, timeout),
18
- killChildren(childPids, timeout),
19
- ]);
20
- }
21
-
22
- // kill process, if SIGTERM not work, try SIGKILL
23
- async function killProcess(subProcess: SubProcess, timeout: number) {
24
- // https://github.com/nodejs/node/pull/34312
25
- (subProcess.process ?? subProcess).kill('SIGTERM');
26
- await Promise.race([
27
- once(subProcess, 'exit'),
28
- sleep(timeout),
29
- ]);
30
- if (subProcess.killed) {
31
- return;
32
- }
33
- // SIGKILL: http://man7.org/linux/man-pages/man7/signal.7.html
34
- // worker: https://github.com/nodejs/node/blob/master/lib/internal/cluster/worker.js#L22
35
- // subProcess.kill is wrapped to subProcess.destroy, it will wait to disconnected.
36
- (subProcess.process ?? subProcess).kill('SIGKILL');
37
- }
38
-
39
- // kill all children processes, if SIGTERM not work, try SIGKILL
40
- async function killChildren(childrenPids: number[], timeout: number) {
41
- if (childrenPids.length === 0) {
42
- return;
43
- }
44
- kill(childrenPids, 'SIGTERM');
45
-
46
- const start = Date.now();
47
- // if timeout is 1000, it will check twice.
48
- const checkInterval = 400;
49
- let unterminated: number[] = [];
50
-
51
- while (Date.now() - start < timeout - checkInterval) {
52
- await sleep(checkInterval);
53
- unterminated = getUnterminatedProcesses(childrenPids);
54
- if (unterminated.length === 0) {
55
- return;
56
- }
57
- }
58
- kill(unterminated, 'SIGKILL');
59
- }
60
-
61
- async function getChildPids(pid: number) {
62
- let childrenPids: number[] = [];
63
- try {
64
- const children = await pstree(pid);
65
- childrenPids = children!.map(c => parseInt(c.PID));
66
- } catch (err) {
67
- // if get children error, just ignore it
68
- debug('pstree %s error: %s, ignore it', pid, err);
69
- }
70
- return childrenPids;
71
- }
72
-
73
- function kill(pids: number[], signal: string) {
74
- for (const pid of pids) {
75
- try {
76
- process.kill(pid, signal);
77
- } catch (err) {
78
- // ignore
79
- debug('kill %s error: %s, signal: %s, ignore it', pid, err, signal);
80
- }
81
- }
82
- }
83
-
84
- function getUnterminatedProcesses(pids: number[]) {
85
- return pids.filter(pid => {
86
- try {
87
- // success means it's still alive
88
- process.kill(pid, 0);
89
- return true;
90
- } catch (err) {
91
- // error means it's dead
92
- debug('kill %s error: %s, it still alive', pid, err);
93
- return false;
94
- }
95
- });
96
- }
97
-
@@ -1,87 +0,0 @@
1
- import { EventEmitter } from 'node:events';
2
- import { BaseAgentWorker } from './mode/base/agent.js';
3
- import { BaseAppWorker } from './mode/base/app.js';
4
-
5
- // worker manager to record agent and worker forked by egg-cluster
6
- // can do some check stuff here to monitor the healthy
7
- export class WorkerManager extends EventEmitter {
8
- agent: BaseAgentWorker | null;
9
- workers = new Map<number, BaseAppWorker>();
10
- exception = 0;
11
- timer: NodeJS.Timeout;
12
-
13
- constructor() {
14
- super();
15
- this.agent = null;
16
- }
17
-
18
- getWorkers() {
19
- return Array.from(this.workers.keys());
20
- }
21
-
22
- setAgent(agent: BaseAgentWorker) {
23
- this.agent = agent;
24
- }
25
-
26
- getAgent() {
27
- return this.agent;
28
- }
29
-
30
- deleteAgent() {
31
- this.agent = null;
32
- }
33
-
34
- setWorker(worker: BaseAppWorker) {
35
- this.workers.set(worker.workerId, worker);
36
- }
37
-
38
- getWorker(workerId: number) {
39
- return this.workers.get(workerId);
40
- }
41
-
42
- deleteWorker(workerId: number) {
43
- this.workers.delete(workerId);
44
- }
45
-
46
- listWorkerIds() {
47
- return Array.from(this.workers.keys());
48
- }
49
-
50
- listWorkers() {
51
- return Array.from(this.workers.values());
52
- }
53
-
54
- getListeningWorkerIds() {
55
- const keys = [];
56
- for (const [ id, worker ] of this.workers.entries()) {
57
- if (worker.state === 'listening') {
58
- keys.push(id);
59
- }
60
- }
61
- return keys;
62
- }
63
-
64
- count() {
65
- return {
66
- agent: this.agent?.status === 'started' ? 1 : 0,
67
- worker: this.listWorkerIds().length,
68
- };
69
- }
70
-
71
- // check agent and worker must both alive
72
- // if exception appear 3 times, emit an exception event
73
- startCheck() {
74
- this.timer = setInterval(() => {
75
- const count = this.count();
76
- if (count.agent > 0 && count.worker > 0) {
77
- this.exception = 0;
78
- return;
79
- }
80
- this.exception++;
81
- if (this.exception >= 3) {
82
- this.emit('exception', count);
83
- clearInterval(this.timer);
84
- }
85
- }, 10000);
86
- }
87
- }