@eggjs/cluster 4.0.0-beta.19 → 4.0.0-beta.21

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