@eggjs/cluster 4.0.0-beta.19 → 4.0.0-beta.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-griHEaCW.js +246 -0
- package/dist/agent_worker.js +2 -2
- package/dist/app-5Was1vub.js +315 -0
- package/dist/app_worker.js +2 -2
- package/dist/index.d.ts +440 -5
- package/dist/index.js +692 -4
- package/dist/{utils/terminate.js → terminate-w3g0oQgq.js} +10 -1
- package/package.json +5 -5
- package/dist/dirname.js +0 -11
- package/dist/error/ClusterAgentWorkerError.d.ts +0 -13
- package/dist/error/ClusterAgentWorkerError.js +0 -22
- package/dist/error/ClusterWorkerExceptionError.d.ts +0 -10
- package/dist/error/ClusterWorkerExceptionError.js +0 -17
- package/dist/master.d.ts +0 -96
- package/dist/master.js +0 -426
- package/dist/utils/messenger.d.ts +0 -96
- package/dist/utils/messenger.js +0 -144
- package/dist/utils/mode/base/agent.d.ts +0 -45
- package/dist/utils/mode/base/agent.js +0 -63
- package/dist/utils/mode/base/app.d.ts +0 -56
- package/dist/utils/mode/base/app.js +0 -77
- package/dist/utils/mode/impl/process/agent.d.ts +0 -22
- package/dist/utils/mode/impl/process/agent.js +0 -93
- package/dist/utils/mode/impl/process/app.d.ts +0 -12
- package/dist/utils/mode/impl/process/app.js +0 -117
- package/dist/utils/mode/impl/worker_threads/agent.d.ts +0 -22
- package/dist/utils/mode/impl/worker_threads/agent.js +0 -79
- package/dist/utils/mode/impl/worker_threads/app.d.ts +0 -13
- package/dist/utils/mode/impl/worker_threads/app.js +0 -128
- package/dist/utils/options.d.ts +0 -83
- package/dist/utils/options.js +0 -56
- package/dist/utils/worker_manager.d.ts +0 -32
- package/dist/utils/worker_manager.js +0 -68
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
import { debuglog } from "node:util";
|
|
2
|
+
import path from "node:path";
|
|
2
3
|
import { once } from "node:events";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
3
5
|
import { ChildProcess } from "node:child_process";
|
|
4
6
|
import { setTimeout } from "node:timers/promises";
|
|
5
7
|
import { pstree } from "@fengmk2/ps-tree";
|
|
6
8
|
|
|
9
|
+
//#region src/dirname.ts
|
|
10
|
+
function getSrcDirname() {
|
|
11
|
+
if (typeof __dirname !== "undefined") return __dirname;
|
|
12
|
+
return path.dirname(fileURLToPath(import.meta.url));
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
//#endregion
|
|
7
16
|
//#region src/utils/terminate.ts
|
|
8
17
|
const debug = debuglog("egg/cluster/utils/terminate");
|
|
9
18
|
async function terminate(subProcess, timeout) {
|
|
@@ -59,4 +68,4 @@ function getUnterminatedProcesses(pids) {
|
|
|
59
68
|
}
|
|
60
69
|
|
|
61
70
|
//#endregion
|
|
62
|
-
export { terminate };
|
|
71
|
+
export { getSrcDirname, terminate };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eggjs/cluster",
|
|
3
|
-
"version": "4.0.0-beta.
|
|
3
|
+
"version": "4.0.0-beta.20",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"sendmessage": "^3.0.1",
|
|
46
46
|
"terminal-link": "^5.0.0",
|
|
47
47
|
"utility": "^2.5.0",
|
|
48
|
-
"@eggjs/utils": "5.0.0-beta.
|
|
48
|
+
"@eggjs/utils": "5.0.0-beta.20"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"address": "2",
|
|
@@ -55,9 +55,9 @@
|
|
|
55
55
|
"typescript": "^5.9.3",
|
|
56
56
|
"urllib": "^4.8.2",
|
|
57
57
|
"vitest": "4.0.0-beta.16",
|
|
58
|
-
"@eggjs/supertest": "9.0.0-beta.
|
|
59
|
-
"@eggjs/
|
|
60
|
-
"@eggjs/
|
|
58
|
+
"@eggjs/supertest": "9.0.0-beta.20",
|
|
59
|
+
"@eggjs/mock": "7.0.0-beta.20",
|
|
60
|
+
"@eggjs/tsconfig": "3.1.0-beta.20"
|
|
61
61
|
},
|
|
62
62
|
"engines": {
|
|
63
63
|
"node": ">=22.18.0"
|
package/dist/dirname.js
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
|
-
import { fileURLToPath } from "node:url";
|
|
3
|
-
|
|
4
|
-
//#region src/dirname.ts
|
|
5
|
-
function getSrcDirname() {
|
|
6
|
-
if (typeof __dirname !== "undefined") return __dirname;
|
|
7
|
-
return path.dirname(fileURLToPath(import.meta.url));
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
//#endregion
|
|
11
|
-
export { getSrcDirname };
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
//#region src/error/ClusterAgentWorkerError.d.ts
|
|
2
|
-
declare class ClusterAgentWorkerError extends Error {
|
|
3
|
-
id: number;
|
|
4
|
-
/**
|
|
5
|
-
* pid in process mode
|
|
6
|
-
* tid in worker_threads mode
|
|
7
|
-
*/
|
|
8
|
-
workerId: number;
|
|
9
|
-
status: string;
|
|
10
|
-
constructor(id: number, workerId: number, status: string, error: Error);
|
|
11
|
-
}
|
|
12
|
-
//#endregion
|
|
13
|
-
export { ClusterAgentWorkerError };
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
//#region src/error/ClusterAgentWorkerError.ts
|
|
2
|
-
var ClusterAgentWorkerError = class extends Error {
|
|
3
|
-
id;
|
|
4
|
-
/**
|
|
5
|
-
* pid in process mode
|
|
6
|
-
* tid in worker_threads mode
|
|
7
|
-
*/
|
|
8
|
-
workerId;
|
|
9
|
-
status;
|
|
10
|
-
constructor(id, workerId, status, error) {
|
|
11
|
-
const message = `Got agent worker error: ${error.message}`;
|
|
12
|
-
super(message, { cause: error });
|
|
13
|
-
this.name = this.constructor.name;
|
|
14
|
-
this.id = id;
|
|
15
|
-
this.workerId = workerId;
|
|
16
|
-
this.status = status;
|
|
17
|
-
Error.captureStackTrace(this, this.constructor);
|
|
18
|
-
}
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
//#endregion
|
|
22
|
-
export { ClusterAgentWorkerError };
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
//#region src/error/ClusterWorkerExceptionError.d.ts
|
|
2
|
-
declare class ClusterWorkerExceptionError extends Error {
|
|
3
|
-
count: {
|
|
4
|
-
agent: number;
|
|
5
|
-
worker: number;
|
|
6
|
-
};
|
|
7
|
-
constructor(agent: number, worker: number);
|
|
8
|
-
}
|
|
9
|
-
//#endregion
|
|
10
|
-
export { ClusterWorkerExceptionError };
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
//#region src/error/ClusterWorkerExceptionError.ts
|
|
2
|
-
var ClusterWorkerExceptionError = class extends Error {
|
|
3
|
-
count;
|
|
4
|
-
constructor(agent, worker) {
|
|
5
|
-
const message = `[master] ${agent} agent and ${worker} worker(s) alive, exit to avoid unknown state`;
|
|
6
|
-
super(message);
|
|
7
|
-
this.name = this.constructor.name;
|
|
8
|
-
this.count = {
|
|
9
|
-
agent,
|
|
10
|
-
worker
|
|
11
|
-
};
|
|
12
|
-
Error.captureStackTrace(this, this.constructor);
|
|
13
|
-
}
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
//#endregion
|
|
17
|
-
export { ClusterWorkerExceptionError };
|
package/dist/master.d.ts
DELETED
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
import { WorkerManager } from "./utils/worker_manager.js";
|
|
2
|
-
import { Messenger } from "./utils/messenger.js";
|
|
3
|
-
import { BaseAppWorker } from "./utils/mode/base/app.js";
|
|
4
|
-
import { ClusterOptions, ParsedClusterOptions } from "./utils/options.js";
|
|
5
|
-
import { AgentProcessUtils } from "./utils/mode/impl/process/agent.js";
|
|
6
|
-
import { AppProcessUtils } from "./utils/mode/impl/process/app.js";
|
|
7
|
-
import { AgentThreadUtils } from "./utils/mode/impl/worker_threads/agent.js";
|
|
8
|
-
import { AppThreadUtils } from "./utils/mode/impl/worker_threads/app.js";
|
|
9
|
-
import { ReadyEventEmitter } from "get-ready";
|
|
10
|
-
import { EggConsoleLogger } from "egg-logger";
|
|
11
|
-
import * as worker_threads0 from "worker_threads";
|
|
12
|
-
import * as cluster0 from "cluster";
|
|
13
|
-
|
|
14
|
-
//#region src/master.d.ts
|
|
15
|
-
interface MasterOptions extends ParsedClusterOptions {
|
|
16
|
-
clusterPort?: number;
|
|
17
|
-
stickyWorkerPort?: number;
|
|
18
|
-
}
|
|
19
|
-
declare class Master extends ReadyEventEmitter {
|
|
20
|
-
#private;
|
|
21
|
-
options: MasterOptions;
|
|
22
|
-
isStarted: boolean;
|
|
23
|
-
workerManager: WorkerManager;
|
|
24
|
-
messenger: Messenger;
|
|
25
|
-
isProduction: boolean;
|
|
26
|
-
agentWorkerIndex: number;
|
|
27
|
-
closed: boolean;
|
|
28
|
-
logger: EggConsoleLogger;
|
|
29
|
-
agentWorker: AgentProcessUtils | AgentThreadUtils;
|
|
30
|
-
appWorker: AppProcessUtils | AppThreadUtils;
|
|
31
|
-
constructor(options?: ClusterOptions);
|
|
32
|
-
startByProcess(): void;
|
|
33
|
-
startByWorkerThreads(): void;
|
|
34
|
-
detectPorts(): Promise<void>;
|
|
35
|
-
log(msg: string, ...args: any[]): void;
|
|
36
|
-
startMasterSocketServer(cb: (err?: Error) => void): void;
|
|
37
|
-
stickyWorker(ip: string): BaseAppWorker<worker_threads0.Worker | cluster0.Worker>;
|
|
38
|
-
forkAgentWorker(): void;
|
|
39
|
-
forkAppWorkers(): void;
|
|
40
|
-
/**
|
|
41
|
-
* close agent worker, App Worker will closed by cluster
|
|
42
|
-
*
|
|
43
|
-
* https://www.exratione.com/2013/05/die-child-process-die/
|
|
44
|
-
* make sure Agent Worker exit before master exit
|
|
45
|
-
*
|
|
46
|
-
* @param {number} timeout - kill agent timeout
|
|
47
|
-
* @return {Promise} -
|
|
48
|
-
*/
|
|
49
|
-
killAgentWorker(timeout: number): Promise<void>;
|
|
50
|
-
killAppWorkers(timeout: number): Promise<void>;
|
|
51
|
-
/**
|
|
52
|
-
* Agent Worker exit handler
|
|
53
|
-
* Will exit during startup, and refork during running.
|
|
54
|
-
*/
|
|
55
|
-
onAgentExit(data: {
|
|
56
|
-
/** exit code */
|
|
57
|
-
code: number;
|
|
58
|
-
/** received signal */
|
|
59
|
-
signal: string;
|
|
60
|
-
}): void;
|
|
61
|
-
onAgentStart(): void;
|
|
62
|
-
/**
|
|
63
|
-
* App Worker exit handler
|
|
64
|
-
*/
|
|
65
|
-
onAppExit(data: {
|
|
66
|
-
workerId: number;
|
|
67
|
-
code: number;
|
|
68
|
-
signal: string;
|
|
69
|
-
}): void;
|
|
70
|
-
/**
|
|
71
|
-
* after app worker
|
|
72
|
-
*/
|
|
73
|
-
onAppStart(data: {
|
|
74
|
-
workerId: number;
|
|
75
|
-
address: ListeningAddress;
|
|
76
|
-
}): void;
|
|
77
|
-
/**
|
|
78
|
-
* master exit handler
|
|
79
|
-
*/
|
|
80
|
-
onExit(code: number): void;
|
|
81
|
-
onSignal(signal: string): void;
|
|
82
|
-
/**
|
|
83
|
-
* reload workers, for develop purpose
|
|
84
|
-
*/
|
|
85
|
-
onReload(): void;
|
|
86
|
-
close(): Promise<void>;
|
|
87
|
-
_doClose(): Promise<void>;
|
|
88
|
-
}
|
|
89
|
-
interface ListeningAddress {
|
|
90
|
-
port: number;
|
|
91
|
-
protocol: string;
|
|
92
|
-
address?: string;
|
|
93
|
-
addressType?: number;
|
|
94
|
-
}
|
|
95
|
-
//#endregion
|
|
96
|
-
export { Master, MasterOptions };
|
package/dist/master.js
DELETED
|
@@ -1,426 +0,0 @@
|
|
|
1
|
-
import { parseOptions } from "./utils/options.js";
|
|
2
|
-
import { WorkerManager } from "./utils/worker_manager.js";
|
|
3
|
-
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";
|
|
8
|
-
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
|
-
};
|
|
410
|
-
function isProduction(options) {
|
|
411
|
-
if (options.env) return options.env !== "local" && options.env !== "unittest";
|
|
412
|
-
return process.env.NODE_ENV === "production";
|
|
413
|
-
}
|
|
414
|
-
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}`;
|
|
420
|
-
}
|
|
421
|
-
function isUnixSock(address) {
|
|
422
|
-
return address.addressType === -1;
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
//#endregion
|
|
426
|
-
export { Master };
|