@eggjs/cluster 3.0.1 → 3.1.0-beta.3

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 (123) hide show
  1. package/README.md +17 -21
  2. package/dist/dirname.js +11 -0
  3. package/dist/error/ClusterAgentWorkerError.d.ts +13 -0
  4. package/dist/error/ClusterAgentWorkerError.js +22 -0
  5. package/dist/error/ClusterWorkerExceptionError.d.ts +10 -0
  6. package/dist/error/ClusterWorkerExceptionError.js +17 -0
  7. package/dist/index.d.ts +22 -0
  8. package/dist/index.js +24 -0
  9. package/dist/master.d.ts +96 -0
  10. package/dist/master.js +426 -0
  11. package/dist/utils/messenger.d.ts +96 -0
  12. package/dist/utils/messenger.js +144 -0
  13. package/dist/utils/mode/base/agent.d.ts +45 -0
  14. package/dist/utils/mode/base/agent.js +63 -0
  15. package/dist/utils/mode/base/app.d.ts +56 -0
  16. package/dist/utils/mode/base/app.js +77 -0
  17. package/dist/utils/mode/impl/process/agent.d.ts +22 -0
  18. package/dist/utils/mode/impl/process/agent.js +93 -0
  19. package/dist/utils/mode/impl/process/app.d.ts +12 -0
  20. package/dist/utils/mode/impl/process/app.js +117 -0
  21. package/dist/utils/mode/impl/worker_threads/agent.d.ts +22 -0
  22. package/dist/utils/mode/impl/worker_threads/agent.js +79 -0
  23. package/dist/utils/mode/impl/worker_threads/app.d.ts +13 -0
  24. package/dist/utils/mode/impl/worker_threads/app.js +128 -0
  25. package/dist/utils/options.d.ts +83 -0
  26. package/dist/utils/options.js +56 -0
  27. package/dist/utils/terminate.js +62 -0
  28. package/dist/utils/worker_manager.d.ts +32 -0
  29. package/dist/utils/worker_manager.js +68 -0
  30. package/package.json +35 -61
  31. package/dist/commonjs/agent_worker.d.ts +0 -1
  32. package/dist/commonjs/agent_worker.js +0 -69
  33. package/dist/commonjs/app_worker.d.ts +0 -1
  34. package/dist/commonjs/app_worker.js +0 -173
  35. package/dist/commonjs/dirname.d.ts +0 -1
  36. package/dist/commonjs/dirname.js +0 -17
  37. package/dist/commonjs/error/ClusterAgentWorkerError.d.ts +0 -10
  38. package/dist/commonjs/error/ClusterAgentWorkerError.js +0 -23
  39. package/dist/commonjs/error/ClusterWorkerExceptionError.d.ts +0 -7
  40. package/dist/commonjs/error/ClusterWorkerExceptionError.js +0 -18
  41. package/dist/commonjs/error/index.d.ts +0 -2
  42. package/dist/commonjs/error/index.js +0 -19
  43. package/dist/commonjs/index.d.ts +0 -17
  44. package/dist/commonjs/index.js +0 -37
  45. package/dist/commonjs/master.d.ts +0 -90
  46. package/dist/commonjs/master.js +0 -560
  47. package/dist/commonjs/package.json +0 -3
  48. package/dist/commonjs/utils/messenger.d.ts +0 -92
  49. package/dist/commonjs/utils/messenger.js +0 -186
  50. package/dist/commonjs/utils/mode/base/agent.d.ts +0 -38
  51. package/dist/commonjs/utils/mode/base/agent.js +0 -68
  52. package/dist/commonjs/utils/mode/base/app.d.ts +0 -48
  53. package/dist/commonjs/utils/mode/base/app.js +0 -83
  54. package/dist/commonjs/utils/mode/impl/process/agent.d.ts +0 -18
  55. package/dist/commonjs/utils/mode/impl/process/agent.js +0 -108
  56. package/dist/commonjs/utils/mode/impl/process/app.d.ts +0 -21
  57. package/dist/commonjs/utils/mode/impl/process/app.js +0 -127
  58. package/dist/commonjs/utils/mode/impl/worker_threads/agent.d.ts +0 -18
  59. package/dist/commonjs/utils/mode/impl/worker_threads/agent.js +0 -91
  60. package/dist/commonjs/utils/mode/impl/worker_threads/app.d.ts +0 -26
  61. package/dist/commonjs/utils/mode/impl/worker_threads/app.js +0 -142
  62. package/dist/commonjs/utils/options.d.ts +0 -80
  63. package/dist/commonjs/utils/options.js +0 -83
  64. package/dist/commonjs/utils/terminate.d.ts +0 -6
  65. package/dist/commonjs/utils/terminate.js +0 -89
  66. package/dist/commonjs/utils/worker_manager.d.ts +0 -25
  67. package/dist/commonjs/utils/worker_manager.js +0 -76
  68. package/dist/esm/agent_worker.d.ts +0 -1
  69. package/dist/esm/agent_worker.js +0 -67
  70. package/dist/esm/app_worker.d.ts +0 -1
  71. package/dist/esm/app_worker.js +0 -168
  72. package/dist/esm/dirname.d.ts +0 -1
  73. package/dist/esm/dirname.js +0 -11
  74. package/dist/esm/error/ClusterAgentWorkerError.d.ts +0 -10
  75. package/dist/esm/error/ClusterAgentWorkerError.js +0 -19
  76. package/dist/esm/error/ClusterWorkerExceptionError.d.ts +0 -7
  77. package/dist/esm/error/ClusterWorkerExceptionError.js +0 -14
  78. package/dist/esm/error/index.d.ts +0 -2
  79. package/dist/esm/error/index.js +0 -3
  80. package/dist/esm/index.d.ts +0 -17
  81. package/dist/esm/index.js +0 -19
  82. package/dist/esm/master.d.ts +0 -90
  83. package/dist/esm/master.js +0 -553
  84. package/dist/esm/package.json +0 -3
  85. package/dist/esm/utils/messenger.d.ts +0 -92
  86. package/dist/esm/utils/messenger.js +0 -179
  87. package/dist/esm/utils/mode/base/agent.d.ts +0 -38
  88. package/dist/esm/utils/mode/base/agent.js +0 -60
  89. package/dist/esm/utils/mode/base/app.d.ts +0 -48
  90. package/dist/esm/utils/mode/base/app.js +0 -75
  91. package/dist/esm/utils/mode/impl/process/agent.d.ts +0 -18
  92. package/dist/esm/utils/mode/impl/process/agent.js +0 -103
  93. package/dist/esm/utils/mode/impl/process/app.d.ts +0 -21
  94. package/dist/esm/utils/mode/impl/process/app.js +0 -119
  95. package/dist/esm/utils/mode/impl/worker_threads/agent.d.ts +0 -18
  96. package/dist/esm/utils/mode/impl/worker_threads/agent.js +0 -83
  97. package/dist/esm/utils/mode/impl/worker_threads/app.d.ts +0 -26
  98. package/dist/esm/utils/mode/impl/worker_threads/app.js +0 -137
  99. package/dist/esm/utils/options.d.ts +0 -80
  100. package/dist/esm/utils/options.js +0 -77
  101. package/dist/esm/utils/terminate.d.ts +0 -6
  102. package/dist/esm/utils/terminate.js +0 -86
  103. package/dist/esm/utils/worker_manager.d.ts +0 -25
  104. package/dist/esm/utils/worker_manager.js +0 -72
  105. package/dist/package.json +0 -4
  106. package/src/agent_worker.ts +0 -80
  107. package/src/app_worker.ts +0 -196
  108. package/src/dirname.ts +0 -11
  109. package/src/error/ClusterAgentWorkerError.ts +0 -19
  110. package/src/error/ClusterWorkerExceptionError.ts +0 -17
  111. package/src/error/index.ts +0 -2
  112. package/src/index.ts +0 -26
  113. package/src/master.ts +0 -658
  114. package/src/utils/messenger.ts +0 -207
  115. package/src/utils/mode/base/agent.ts +0 -90
  116. package/src/utils/mode/base/app.ts +0 -119
  117. package/src/utils/mode/impl/process/agent.ts +0 -119
  118. package/src/utils/mode/impl/process/app.ts +0 -140
  119. package/src/utils/mode/impl/worker_threads/agent.ts +0 -99
  120. package/src/utils/mode/impl/worker_threads/app.ts +0 -164
  121. package/src/utils/options.ts +0 -171
  122. package/src/utils/terminate.ts +0 -97
  123. package/src/utils/worker_manager.ts +0 -87
@@ -1,207 +0,0 @@
1
- import { debuglog } from 'node:util';
2
- import workerThreads from 'node:worker_threads';
3
- import type { Master } from '../master.js';
4
- import type { WorkerManager } from './worker_manager.js';
5
-
6
- const debug = debuglog('@eggjs/cluster/messenger');
7
-
8
- export type MessageCharacter = 'agent' | 'app' | 'master' | 'parent';
9
-
10
- export interface MessageBody {
11
- action: string;
12
- data?: unknown;
13
- to?: MessageCharacter;
14
- from?: MessageCharacter;
15
- /**
16
- * @deprecated Keep compatible, please use receiverWorkerId instead
17
- */
18
- receiverPid?: string;
19
- receiverWorkerId?: string;
20
- senderWorkerId?: string;
21
- }
22
-
23
- /**
24
- * master messenger, provide communication between parent, master, agent and app.
25
- *
26
- * ┌────────┐
27
- * │ parent │
28
- * /└────────┘\
29
- * / | \
30
- * / ┌────────┐ \
31
- * / │ master │ \
32
- * / └────────┘ \
33
- * / / \ \
34
- * ┌───────┐ ┌───────┐
35
- * │ agent │ ------- │ app │
36
- * └───────┘ └───────┘
37
- *
38
- *
39
- * in app worker
40
- *
41
- * ```js
42
- * process.send({
43
- * action: 'xxx',
44
- * data: '',
45
- * to: 'agent/master/parent', // default to agent
46
- * });
47
- * ```
48
- *
49
- * in agent worker
50
- *
51
- * ```js
52
- * process.send({
53
- * action: 'xxx',
54
- * data: '',
55
- * to: 'app/master/parent', // default to app
56
- * });
57
- * ```
58
- *
59
- * in parent
60
- *
61
- * ```js
62
- * process.send({
63
- * action: 'xxx',
64
- * data: '',
65
- * to: 'app/agent/master', // default to master
66
- * });
67
- * ```
68
- */
69
- export class Messenger {
70
- #master: Master;
71
- #workerManager: WorkerManager;
72
- #hasParent: boolean;
73
-
74
- constructor(master: Master, workerManager: WorkerManager) {
75
- this.#master = master;
76
- this.#workerManager = workerManager;
77
- this.#hasParent = !!workerThreads.parentPort || !!process.send;
78
- process.on('message', (msg: MessageBody) => {
79
- msg.from = 'parent';
80
- this.send(msg);
81
- });
82
- process.once('disconnect', () => {
83
- this.#hasParent = false;
84
- });
85
- }
86
-
87
- /**
88
- * send message
89
- * @param {Object} data message body
90
- * - {String} from from who
91
- * - {String} to to who
92
- */
93
- send(data: MessageBody) {
94
- if (!data.from) {
95
- data.from = 'master';
96
- }
97
-
98
- // https://github.com/eggjs/egg/blob/b6861f1c7548f05a281386050dfeaeb30f236558/lib/core/messenger/ipc.js#L56
99
- // recognize receiverWorkerId is to who
100
- const receiverWorkerId = data.receiverWorkerId ?? data.receiverPid;
101
- if (receiverWorkerId) {
102
- if (receiverWorkerId === String(process.pid)) {
103
- data.to = 'master';
104
- } else if (receiverWorkerId === String(this.#workerManager.getAgent()!.workerId)) {
105
- data.to = 'agent';
106
- } else {
107
- data.to = 'app';
108
- }
109
- }
110
-
111
- // default from -> to rules
112
- if (!data.to) {
113
- if (data.from === 'agent') {
114
- data.to = 'app';
115
- }
116
- if (data.from === 'app') {
117
- data.to = 'agent';
118
- }
119
- if (data.from === 'parent') {
120
- data.to = 'master';
121
- }
122
- }
123
-
124
- // app -> master
125
- // agent -> master
126
- if (data.to === 'master') {
127
- debug('%s -> master, data: %j', data.from, data);
128
- // app/agent to master
129
- this.sendToMaster(data);
130
- return;
131
- }
132
-
133
- // master -> parent
134
- // app -> parent
135
- // agent -> parent
136
- if (data.to === 'parent') {
137
- debug('%s -> parent, data: %j', data.from, data);
138
- this.sendToParent(data);
139
- return;
140
- }
141
-
142
- // parent -> master -> app
143
- // agent -> master -> app
144
- if (data.to === 'app') {
145
- debug('%s -> %s, data: %j', data.from, data.to, data);
146
- this.sendToAppWorker(data);
147
- return;
148
- }
149
-
150
- // parent -> master -> agent
151
- // app -> master -> agent,可能不指定 to
152
- if (data.to === 'agent') {
153
- debug('%s -> %s, data: %j', data.from, data.to, data);
154
- this.sendToAgentWorker(data);
155
- return;
156
- }
157
- }
158
-
159
- /**
160
- * send message to master self
161
- * @param {Object} data message body
162
- */
163
- sendToMaster(data: MessageBody) {
164
- // e.g: master.on('app-start', data => {})
165
- this.#master.emit(data.action, data.data);
166
- }
167
-
168
- /**
169
- * send message to parent process
170
- * @param {Object} data message body
171
- */
172
- sendToParent(data: MessageBody) {
173
- if (!this.#hasParent) {
174
- return;
175
- }
176
- process.send!(data);
177
- }
178
-
179
- /**
180
- * send message to app worker
181
- * @param {Object} data message body
182
- */
183
- sendToAppWorker(data: MessageBody) {
184
- for (const worker of this.#workerManager.listWorkers()) {
185
- if (worker.state === 'disconnected') {
186
- continue;
187
- }
188
- // check receiverWorkerId
189
- const receiverWorkerId = data.receiverWorkerId ?? data.receiverPid;
190
- if (receiverWorkerId && receiverWorkerId !== String(worker.workerId)) {
191
- continue;
192
- }
193
- worker.send(data);
194
- }
195
- }
196
-
197
- /**
198
- * send message to agent worker
199
- * @param {Object} data message body
200
- */
201
- sendToAgentWorker(data: MessageBody) {
202
- const agent = this.#workerManager.getAgent();
203
- if (agent) {
204
- agent.send(data);
205
- }
206
- }
207
- }
@@ -1,90 +0,0 @@
1
- import path from 'node:path';
2
- import { EventEmitter } from 'node:events';
3
- import type { ChildProcess } from 'node:child_process';
4
- import type { Worker } from 'node:worker_threads';
5
- import type { Logger } from 'egg-logger';
6
- import type { MasterOptions } from '../../../master.js';
7
- import type { MessageBody, Messenger } from '../../messenger.js';
8
- import { getSrcDirname } from '../../../dirname.js';
9
-
10
- export abstract class BaseAgentWorker<T = ChildProcess | Worker> {
11
- instance: T;
12
- #instanceId: number;
13
- #instanceStatus: string;
14
-
15
- constructor(instance: T) {
16
- this.instance = instance;
17
- }
18
-
19
- abstract get workerId(): number;
20
-
21
- get id() {
22
- return this.#instanceId;
23
- }
24
-
25
- set id(id) {
26
- this.#instanceId = id;
27
- }
28
-
29
- get status() {
30
- return this.#instanceStatus;
31
- }
32
-
33
- set status(status) {
34
- this.#instanceStatus = status;
35
- }
36
-
37
- abstract send(message: MessageBody): void;
38
-
39
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
40
- static send(_message: MessageBody) {
41
- throw new Error('BaseAgentWorker should implement send.');
42
- }
43
-
44
- static kill() {
45
- throw new Error('BaseAgentWorker should implement kill.');
46
- }
47
-
48
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
49
- static gracefulExit(_options: any) {
50
- throw new Error('BaseAgentWorker should implement gracefulExit.');
51
- }
52
- }
53
-
54
- type LogFun = (msg: any, ...args: any[]) => void;
55
-
56
- export abstract class BaseAgentUtils extends EventEmitter {
57
- protected options: MasterOptions;
58
- protected messenger: Messenger;
59
- protected log: LogFun;
60
- protected logger: Logger;
61
- // public attrs
62
- startTime = 0;
63
-
64
- constructor(options: MasterOptions, { log, logger, messenger }: {
65
- log: LogFun;
66
- logger: Logger;
67
- messenger: Messenger;
68
- }) {
69
- super();
70
- this.options = options;
71
- this.log = log;
72
- this.logger = logger;
73
- this.messenger = messenger;
74
- // this.instance = null;
75
- }
76
-
77
- getAgentWorkerFile() {
78
- return path.join(getSrcDirname(), 'agent_worker.js');
79
- }
80
-
81
- fork() {
82
- throw new Error('BaseAgent should implement fork.');
83
- }
84
-
85
- clean() {
86
- throw new Error('BaseAgent should implement clean.');
87
- }
88
-
89
- abstract kill(timeout: number): Promise<void>;
90
- }
@@ -1,119 +0,0 @@
1
- import path from 'node:path';
2
- import { EventEmitter } from 'node:events';
3
- import type { Worker as ClusterProcessWorker } from 'node:cluster';
4
- import type { Worker as ThreadWorker } from 'node:worker_threads';
5
- import type { Logger } from 'egg-logger';
6
- import type { MessageBody, Messenger } from '../../messenger.js';
7
- import type { MasterOptions } from '../../../master.js';
8
- import { getSrcDirname } from '../../../dirname.js';
9
-
10
- export abstract class BaseAppWorker<T = ThreadWorker | ClusterProcessWorker> {
11
- instance: T;
12
-
13
- constructor(instance: T) {
14
- this.instance = instance;
15
- }
16
-
17
- abstract get workerId(): number;
18
-
19
- abstract get id(): number;
20
-
21
- get state(): string {
22
- return Reflect.get(this.instance!, 'state') as string;
23
- }
24
-
25
- set state(state: string) {
26
- Reflect.set(this.instance!, 'state', state);
27
- }
28
-
29
- abstract get exitedAfterDisconnect(): boolean;
30
-
31
- abstract get exitCode(): number;
32
-
33
- get disableRefork(): boolean {
34
- return Reflect.get(this.instance!, 'disableRefork') as boolean;
35
- }
36
-
37
- set disableRefork(disableRefork: boolean) {
38
- Reflect.set(this.instance!, 'disableRefork', disableRefork);
39
- }
40
-
41
- get isDevReload(): boolean {
42
- return Reflect.get(this.instance!, 'isDevReload') as boolean;
43
- }
44
-
45
- set isDevReload(isDevReload: boolean) {
46
- Reflect.set(this.instance!, 'isDevReload', isDevReload);
47
- }
48
-
49
- abstract send(data: MessageBody): void;
50
-
51
- clean() {
52
- throw new Error('BaseAppWorker should implement clean.');
53
- }
54
-
55
- // static methods use on src/app_worker.ts
56
-
57
- static get workerId(): number {
58
- throw new Error('BaseAppWorker should implement workerId.');
59
- }
60
-
61
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
62
- static on(..._args: any[]) {
63
- throw new Error('BaseAppWorker should implement on.');
64
- }
65
-
66
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
67
- static send(_message: MessageBody) {
68
- throw new Error('BaseAgentWorker should implement send.');
69
- }
70
-
71
- static kill() {
72
- throw new Error('BaseAppWorker should implement kill.');
73
- }
74
-
75
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
76
- static gracefulExit(_options: any) {
77
- throw new Error('BaseAgentWorker should implement gracefulExit.');
78
- }
79
- }
80
-
81
- type LogFun = (msg: any, ...args: any[]) => void;
82
-
83
- export abstract class BaseAppUtils extends EventEmitter {
84
- options: MasterOptions;
85
- protected messenger: Messenger;
86
- protected log: LogFun;
87
- protected logger: Logger;
88
- protected isProduction: boolean;
89
- // public attrs
90
- startTime = 0;
91
- startSuccessCount = 0;
92
- isAllWorkerStarted = false;
93
-
94
- constructor(options: MasterOptions, {
95
- log, logger, messenger, isProduction,
96
- }: {
97
- log: LogFun;
98
- logger: Logger;
99
- messenger: Messenger;
100
- isProduction: boolean;
101
- }) {
102
- super();
103
- this.options = options;
104
- this.log = log;
105
- this.logger = logger;
106
- this.messenger = messenger;
107
- this.isProduction = isProduction;
108
- }
109
-
110
- getAppWorkerFile() {
111
- return path.join(getSrcDirname(), 'app_worker.js');
112
- }
113
-
114
- fork() {
115
- throw new Error('BaseApp should implement fork.');
116
- }
117
-
118
- abstract kill(timeout: number): Promise<void>;
119
- }
@@ -1,119 +0,0 @@
1
- import { fork, type ChildProcess, type ForkOptions } from 'node:child_process';
2
- import { sendmessage } from 'sendmessage';
3
- import { graceful as gracefulExit, type Options as gracefulExitOptions } from 'graceful-process';
4
- import { BaseAgentWorker, BaseAgentUtils } from '../../base/agent.js';
5
- import { terminate } from '../../../terminate.js';
6
- import type { MessageBody } from '../../../messenger.js';
7
- import { ClusterAgentWorkerError } from '../../../../error/ClusterAgentWorkerError.js';
8
-
9
- export class AgentProcessWorker extends BaseAgentWorker<ChildProcess> {
10
- get workerId() {
11
- return this.instance.pid!;
12
- }
13
-
14
- send(message: MessageBody) {
15
- sendmessage(this.instance, message);
16
- }
17
-
18
- static send(message: MessageBody) {
19
- message.senderWorkerId = String(process.pid);
20
- process.send!(message);
21
- }
22
-
23
- static kill() {
24
- process.exitCode = 1;
25
- process.kill(process.pid);
26
- }
27
-
28
- static gracefulExit(options: gracefulExitOptions) {
29
- gracefulExit(options);
30
- }
31
- }
32
-
33
- export class AgentProcessUtils extends BaseAgentUtils {
34
- #agentProcess: ChildProcess;
35
- #id = 0;
36
- instance: AgentProcessWorker;
37
-
38
- fork() {
39
- this.startTime = Date.now();
40
-
41
- const args = [ JSON.stringify(this.options) ];
42
- const forkOptions: ForkOptions & { windowsHide?: boolean } = {};
43
-
44
- if (process.platform === 'win32') {
45
- forkOptions.windowsHide = true;
46
- }
47
-
48
- // add debug execArgv
49
- const debugPort = process.env.EGG_AGENT_DEBUG_PORT ?? 5800;
50
- if (this.options.isDebug) {
51
- forkOptions.execArgv = process.execArgv.concat([ `--inspect-port=${debugPort}` ]);
52
- }
53
-
54
- const agentProcess = this.#agentProcess = fork(this.getAgentWorkerFile(), args, forkOptions);
55
- const agentWorker = this.instance = new AgentProcessWorker(agentProcess);
56
- agentWorker.status = 'starting';
57
- agentWorker.id = ++this.#id;
58
- this.emit('agent_forked', agentWorker);
59
- this.log('[master] agent_worker#%s:%s start with clusterPort:%s',
60
- agentWorker.id, agentWorker.workerId, this.options.clusterPort);
61
-
62
- // send debug message
63
- if (this.options.isDebug) {
64
- this.messenger.send({
65
- to: 'parent',
66
- from: 'agent',
67
- action: 'debug',
68
- data: {
69
- debugPort,
70
- // keep compatibility, should use workerId instead
71
- pid: agentWorker.workerId,
72
- workerId: agentWorker.workerId,
73
- },
74
- });
75
- }
76
- // forwarding agent' message to messenger
77
- agentProcess.on('message', (msg: MessageBody | string) => {
78
- if (typeof msg === 'string') {
79
- msg = {
80
- action: msg,
81
- data: msg,
82
- };
83
- }
84
- msg.from = 'agent';
85
- this.messenger.send(msg);
86
- });
87
- // logger error event
88
- agentProcess.on('error', err => {
89
- err.name = 'AgentWorkerError';
90
- this.logger.error(new ClusterAgentWorkerError(agentWorker.id, agentWorker.workerId, agentWorker.status, err));
91
- });
92
- // agent exit message
93
- agentProcess.once('exit', (code, signal) => {
94
- this.messenger.send({
95
- action: 'agent-exit',
96
- data: {
97
- code,
98
- signal,
99
- },
100
- to: 'master',
101
- from: 'agent',
102
- });
103
- });
104
-
105
- return this;
106
- }
107
-
108
- clean() {
109
- this.#agentProcess.removeAllListeners();
110
- }
111
-
112
- async kill(timeout: number) {
113
- if (this.#agentProcess) {
114
- this.log('[master] kill agent worker with signal SIGTERM');
115
- this.clean();
116
- await terminate(this.#agentProcess, timeout);
117
- }
118
- }
119
- }
@@ -1,140 +0,0 @@
1
- import cluster, { type Worker as ClusterProcessWorker } from 'node:cluster';
2
- import { cfork } from 'cfork';
3
- import { sendmessage } from 'sendmessage';
4
- import { graceful as gracefulExit, type Options as gracefulExitOptions } from 'graceful-process';
5
- import { BaseAppWorker, BaseAppUtils } from '../../base/app.js';
6
- import { terminate } from '../../../terminate.js';
7
- import type { MessageBody } from '../../../messenger.js';
8
-
9
- export class AppProcessWorker extends BaseAppWorker<ClusterProcessWorker> {
10
- get id() {
11
- return this.instance.id;
12
- }
13
-
14
- get workerId() {
15
- return this.instance.process.pid!;
16
- }
17
-
18
- get exitedAfterDisconnect() {
19
- return this.instance.exitedAfterDisconnect;
20
- }
21
-
22
- get exitCode() {
23
- return this.instance.process.exitCode!;
24
- }
25
-
26
- send(message: MessageBody) {
27
- sendmessage(this.instance, message);
28
- }
29
-
30
- clean() {
31
- this.instance.removeAllListeners();
32
- }
33
-
34
- // static methods use on src/app_worker.ts
35
-
36
- static get workerId() {
37
- return process.pid;
38
- }
39
-
40
- static on(event: string, listener: (...args: any[]) => void) {
41
- process.on(event, listener);
42
- }
43
-
44
- static send(message: MessageBody) {
45
- message.senderWorkerId = String(process.pid);
46
- process.send!(message);
47
- }
48
-
49
- static kill() {
50
- process.exitCode = 1;
51
- process.kill(process.pid);
52
- }
53
-
54
- static gracefulExit(options: gracefulExitOptions) {
55
- gracefulExit(options);
56
- }
57
- }
58
-
59
- export class AppProcessUtils extends BaseAppUtils {
60
- fork() {
61
- this.startTime = Date.now();
62
- this.startSuccessCount = 0;
63
-
64
- const args = [ JSON.stringify(this.options) ];
65
- this.log('[master] start appWorker with args %j (process)', args);
66
- cfork({
67
- exec: this.getAppWorkerFile(),
68
- args,
69
- silent: false,
70
- count: this.options.workers,
71
- // don't refork in local env
72
- refork: this.isProduction,
73
- windowsHide: process.platform === 'win32',
74
- });
75
-
76
- let debugPort = process.debugPort;
77
- cluster.on('fork', worker => {
78
- const appWorker = new AppProcessWorker(worker);
79
- this.emit('worker_forked', appWorker);
80
- appWorker.disableRefork = true;
81
- worker.on('message', msg => {
82
- if (typeof msg === 'string') {
83
- msg = {
84
- action: msg,
85
- data: msg,
86
- };
87
- }
88
- msg.from = 'app';
89
- this.messenger.send(msg);
90
- });
91
- this.log('[master] app_worker#%s:%s start, state: %s, current workers: %j',
92
- appWorker.id, appWorker.workerId, appWorker.state,
93
- Object.keys(cluster.workers!));
94
-
95
- // send debug message, due to `brk` scene, send here instead of app_worker.js
96
- if (this.options.isDebug) {
97
- debugPort++;
98
- this.messenger.send({
99
- to: 'parent',
100
- from: 'app',
101
- action: 'debug',
102
- data: {
103
- debugPort,
104
- // keep compatibility, should use workerId instead
105
- pid: appWorker.workerId,
106
- workerId: appWorker.workerId,
107
- },
108
- });
109
- }
110
- });
111
- cluster.on('disconnect', worker => {
112
- const appWorker = new AppProcessWorker(worker);
113
- this.log('[master] app_worker#%s:%s disconnect, suicide: %s, state: %s, current workers: %j',
114
- appWorker.id, appWorker.workerId, appWorker.exitedAfterDisconnect, appWorker.state,
115
- Object.keys(cluster.workers!));
116
- });
117
- cluster.on('exit', (worker, code, signal) => {
118
- const appWorker = new AppProcessWorker(worker);
119
- this.messenger.send({
120
- action: 'app-exit',
121
- data: {
122
- workerId: appWorker.workerId,
123
- code,
124
- signal,
125
- },
126
- to: 'master',
127
- from: 'app',
128
- });
129
- });
130
- return this;
131
- }
132
-
133
- async kill(timeout: number) {
134
- await Promise.all(Object.keys(cluster.workers!).map(id => {
135
- const worker = cluster.workers![id]!;
136
- Reflect.set(worker, 'disableRefork', true);
137
- return terminate(worker.process, timeout);
138
- }));
139
- }
140
- }