@eggjs/cluster 4.0.0-beta.34 → 4.0.0-beta.36
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent_worker.d.ts +1 -1
- package/dist/agent_worker.js +50 -65
- package/dist/app_worker.d.ts +1 -1
- package/dist/app_worker.js +127 -165
- package/dist/error/ClusterAgentWorkerError.d.ts +12 -9
- package/dist/error/ClusterAgentWorkerError.js +22 -19
- package/dist/error/ClusterWorkerExceptionError.d.ts +9 -6
- package/dist/error/ClusterWorkerExceptionError.js +17 -14
- package/dist/index.d.ts +20 -15
- package/dist/index.js +20 -16
- package/dist/master.d.ts +89 -86
- package/dist/master.js +425 -556
- package/dist/utils/messenger.d.ts +93 -89
- package/dist/utils/messenger.js +143 -178
- package/dist/utils/mode/base/agent.d.ts +42 -35
- package/dist/utils/mode/base/agent.js +63 -65
- package/dist/utils/mode/base/app.d.ts +53 -45
- package/dist/utils/mode/base/app.js +77 -80
- package/dist/utils/mode/impl/process/agent.d.ts +20 -16
- package/dist/utils/mode/impl/process/agent.js +95 -105
- package/dist/utils/mode/impl/process/app.d.ts +23 -19
- package/dist/utils/mode/impl/process/app.js +116 -118
- package/dist/utils/mode/impl/worker_threads/agent.d.ts +20 -16
- package/dist/utils/mode/impl/worker_threads/agent.js +78 -85
- package/dist/utils/mode/impl/worker_threads/app.d.ts +28 -24
- package/dist/utils/mode/impl/worker_threads/app.js +128 -137
- package/dist/utils/options.d.ts +79 -76
- package/dist/utils/options.js +55 -80
- package/dist/utils/terminate.js +50 -70
- package/dist/utils/worker_manager.d.ts +28 -24
- package/dist/utils/worker_manager.js +68 -74
- package/package.json +33 -34
- package/dist/error/index.d.ts +0 -2
- package/dist/error/index.js +0 -3
- package/dist/utils/terminate.d.ts +0 -6
package/dist/utils/options.d.ts
CHANGED
|
@@ -1,80 +1,83 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
import { SecureContextOptions } from "node:tls";
|
|
2
|
+
|
|
3
|
+
//#region src/utils/options.d.ts
|
|
4
|
+
interface ClusterHTTPSSecureOptions {
|
|
5
|
+
key: SecureContextOptions["key"];
|
|
6
|
+
cert: SecureContextOptions["cert"];
|
|
7
|
+
ca?: SecureContextOptions["ca"];
|
|
8
|
+
passphrase?: SecureContextOptions["passphrase"];
|
|
7
9
|
}
|
|
8
|
-
|
|
10
|
+
type ClusterStartMode = "process" | "worker_threads";
|
|
9
11
|
/** Cluster start options */
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
12
|
+
interface ClusterOptions {
|
|
13
|
+
/**
|
|
14
|
+
* specify framework that can be absolute path or npm package
|
|
15
|
+
*/
|
|
16
|
+
framework?: string;
|
|
17
|
+
/**
|
|
18
|
+
* @deprecated please use framework instead
|
|
19
|
+
*/
|
|
20
|
+
customEgg?: string;
|
|
21
|
+
/** directory of application, default to `process.cwd()` */
|
|
22
|
+
baseDir?: string;
|
|
23
|
+
/**
|
|
24
|
+
* numbers of app workers, default to `os.cpus().length`
|
|
25
|
+
*/
|
|
26
|
+
workers?: number | string;
|
|
27
|
+
/**
|
|
28
|
+
* listening port, default to `7001`(http) or `8443`(https)
|
|
29
|
+
*/
|
|
30
|
+
port?: number | string | null;
|
|
31
|
+
/**
|
|
32
|
+
* listening a debug port on http protocol
|
|
33
|
+
*/
|
|
34
|
+
debugPort?: number;
|
|
35
|
+
/**
|
|
36
|
+
* https options, { key, cert, ca }, full path
|
|
37
|
+
*/
|
|
38
|
+
https?: ClusterHTTPSSecureOptions | boolean;
|
|
39
|
+
/**
|
|
40
|
+
* @deprecated please use `options.https.key` instead
|
|
41
|
+
*/
|
|
42
|
+
key?: ClusterHTTPSSecureOptions["key"];
|
|
43
|
+
/**
|
|
44
|
+
* @deprecated please use `options.https.cert` instead
|
|
45
|
+
*/
|
|
46
|
+
cert?: ClusterHTTPSSecureOptions["cert"];
|
|
47
|
+
/**
|
|
48
|
+
* will inject into worker/agent process
|
|
49
|
+
*/
|
|
50
|
+
require?: string | string[];
|
|
51
|
+
/**
|
|
52
|
+
* will save master pid to this file
|
|
53
|
+
*/
|
|
54
|
+
pidFile?: string;
|
|
55
|
+
/**
|
|
56
|
+
* custom env, default is `process.env.EGG_SERVER_ENV`
|
|
57
|
+
*/
|
|
58
|
+
env?: string;
|
|
59
|
+
/**
|
|
60
|
+
* default is `'process'`, use `'worker_threads'` to start the app & agent worker by worker_threads
|
|
61
|
+
*/
|
|
62
|
+
startMode?: ClusterStartMode;
|
|
63
|
+
/**
|
|
64
|
+
* startup port of each app worker, such as: `[7001, 7002, 7003]`, only effects when the startMode is `'worker_threads'`
|
|
65
|
+
*/
|
|
66
|
+
ports?: number[];
|
|
67
|
+
/**
|
|
68
|
+
* sticky mode server
|
|
69
|
+
*/
|
|
70
|
+
sticky?: boolean;
|
|
71
|
+
/** customized plugins, for unittest */
|
|
72
|
+
plugins?: object;
|
|
73
|
+
isDebug?: boolean;
|
|
72
74
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
75
|
+
interface ParsedClusterOptions extends ClusterOptions {
|
|
76
|
+
port?: number;
|
|
77
|
+
baseDir: string;
|
|
78
|
+
workers: number;
|
|
79
|
+
framework: string;
|
|
80
|
+
startMode: ClusterStartMode;
|
|
79
81
|
}
|
|
80
|
-
|
|
82
|
+
//#endregion
|
|
83
|
+
export { ClusterHTTPSSecureOptions, ClusterOptions, ClusterStartMode, ParsedClusterOptions };
|
package/dist/utils/options.js
CHANGED
|
@@ -1,81 +1,56 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import path from
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import {} from
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
if (options.port && typeof options.port === 'string') {
|
|
54
|
-
options.port = parseInt(options.port);
|
|
55
|
-
}
|
|
56
|
-
if (options.port === null) {
|
|
57
|
-
options.port = undefined;
|
|
58
|
-
}
|
|
59
|
-
if (options.workers && typeof options.workers === 'string') {
|
|
60
|
-
options.workers = parseInt(options.workers);
|
|
61
|
-
}
|
|
62
|
-
if (!options.workers) {
|
|
63
|
-
options.workers = os.cpus().length;
|
|
64
|
-
}
|
|
65
|
-
if (options.require) {
|
|
66
|
-
if (typeof options.require === 'string') {
|
|
67
|
-
options.require = [options.require];
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
// don't print deprecated message in production env.
|
|
71
|
-
// it will print to stderr.
|
|
72
|
-
if (process.env.NODE_ENV === 'production') {
|
|
73
|
-
process.env.NO_DEPRECATION = '*';
|
|
74
|
-
}
|
|
75
|
-
const isDebug = process.execArgv.some((argv) => argv.includes('--debug') || argv.includes('--inspect'));
|
|
76
|
-
if (isDebug) {
|
|
77
|
-
options.isDebug = isDebug;
|
|
78
|
-
}
|
|
79
|
-
return options;
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { debuglog } from "node:util";
|
|
5
|
+
import assert from "node:assert";
|
|
6
|
+
import { getFrameworkPath, importModule } from "@eggjs/utils";
|
|
7
|
+
|
|
8
|
+
//#region src/utils/options.ts
|
|
9
|
+
const debug = debuglog("egg/cluster/utils/options");
|
|
10
|
+
async function parseOptions(options) {
|
|
11
|
+
options = {
|
|
12
|
+
baseDir: process.cwd(),
|
|
13
|
+
port: options?.https ? 8443 : void 0,
|
|
14
|
+
startMode: "process",
|
|
15
|
+
env: process.env.EGG_SERVER_ENV,
|
|
16
|
+
...options
|
|
17
|
+
};
|
|
18
|
+
const pkgPath = path.join(options.baseDir, "package.json");
|
|
19
|
+
assert(fs.existsSync(pkgPath), `${pkgPath} should exist`);
|
|
20
|
+
options.framework = getFrameworkPath({
|
|
21
|
+
baseDir: options.baseDir,
|
|
22
|
+
framework: options.framework ?? options.customEgg
|
|
23
|
+
});
|
|
24
|
+
debug("[parseOptions] %o", options);
|
|
25
|
+
const egg = await importModule(options.framework, { paths: [options.baseDir] });
|
|
26
|
+
assert(egg.Application, `should define Application in ${options.framework}`);
|
|
27
|
+
assert(egg.Agent, `should define Agent in ${options.framework}`);
|
|
28
|
+
if (options.https === true) {
|
|
29
|
+
console.warn("[@eggjs/cluster:deprecated] [master] Please use `https: { key, cert, ca }` instead of `https: true`");
|
|
30
|
+
options.https = {
|
|
31
|
+
key: options.key,
|
|
32
|
+
cert: options.cert
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
if (options.https) {
|
|
36
|
+
assert(options.https.key, "options.https.key should exists");
|
|
37
|
+
if (typeof options.https.key === "string") assert(fs.existsSync(options.https.key), "options.https.key file should exists");
|
|
38
|
+
assert(options.https.cert, "options.https.cert should exists");
|
|
39
|
+
if (typeof options.https.cert === "string") assert(fs.existsSync(options.https.cert), "options.https.cert file should exists");
|
|
40
|
+
if (typeof options.https.ca === "string") assert(fs.existsSync(options.https.ca), "options.https.ca file should exists");
|
|
41
|
+
}
|
|
42
|
+
if (options.port && typeof options.port === "string") options.port = parseInt(options.port);
|
|
43
|
+
if (options.port === null) options.port = void 0;
|
|
44
|
+
if (options.workers && typeof options.workers === "string") options.workers = parseInt(options.workers);
|
|
45
|
+
if (!options.workers) options.workers = os.cpus().length;
|
|
46
|
+
if (options.require) {
|
|
47
|
+
if (typeof options.require === "string") options.require = [options.require];
|
|
48
|
+
}
|
|
49
|
+
if (process.env.NODE_ENV === "production") process.env.NO_DEPRECATION = "*";
|
|
50
|
+
const isDebug = process.execArgv.some((argv) => argv.includes("--debug") || argv.includes("--inspect"));
|
|
51
|
+
if (isDebug) options.isDebug = isDebug;
|
|
52
|
+
return options;
|
|
80
53
|
}
|
|
81
|
-
|
|
54
|
+
|
|
55
|
+
//#endregion
|
|
56
|
+
export { parseOptions };
|
package/dist/utils/terminate.js
CHANGED
|
@@ -1,81 +1,61 @@
|
|
|
1
|
-
import { debuglog } from
|
|
2
|
-
import {
|
|
3
|
-
import { once } from
|
|
4
|
-
import {
|
|
5
|
-
import { pstree } from
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
1
|
+
import { debuglog } from "node:util";
|
|
2
|
+
import { ChildProcess } from "node:child_process";
|
|
3
|
+
import { once } from "node:events";
|
|
4
|
+
import { setTimeout } from "node:timers/promises";
|
|
5
|
+
import { pstree } from "@fengmk2/ps-tree";
|
|
6
|
+
|
|
7
|
+
//#region src/utils/terminate.ts
|
|
8
|
+
const debug = debuglog("egg/cluster/utils/terminate");
|
|
9
|
+
async function terminate(subProcess, timeout) {
|
|
10
|
+
const childPids = await getChildPids(subProcess.process?.pid ?? subProcess.pid);
|
|
11
|
+
await Promise.all([killProcess(subProcess, timeout), killChildren(childPids, timeout)]);
|
|
11
12
|
}
|
|
12
|
-
// kill process, if SIGTERM not work, try SIGKILL
|
|
13
13
|
async function killProcess(subProcess, timeout) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
return;
|
|
19
|
-
}
|
|
20
|
-
// SIGKILL: http://man7.org/linux/man-pages/man7/signal.7.html
|
|
21
|
-
// worker: https://github.com/nodejs/node/blob/master/lib/internal/cluster/worker.js#L22
|
|
22
|
-
// subProcess.kill is wrapped to subProcess.destroy, it will wait to disconnected.
|
|
23
|
-
(subProcess.process ?? subProcess).kill('SIGKILL');
|
|
14
|
+
(subProcess.process ?? subProcess).kill("SIGTERM");
|
|
15
|
+
await Promise.race([once(subProcess, "exit"), setTimeout(timeout)]);
|
|
16
|
+
if (subProcess.killed) return;
|
|
17
|
+
(subProcess.process ?? subProcess).kill("SIGKILL");
|
|
24
18
|
}
|
|
25
|
-
// kill all children processes, if SIGTERM not work, try SIGKILL
|
|
26
19
|
async function killChildren(childrenPids, timeout) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
if (unterminated.length === 0) {
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
kill(unterminated, 'SIGKILL');
|
|
20
|
+
if (childrenPids.length === 0) return;
|
|
21
|
+
kill(childrenPids, "SIGTERM");
|
|
22
|
+
const start = Date.now();
|
|
23
|
+
const checkInterval = 400;
|
|
24
|
+
let unterminated = [];
|
|
25
|
+
while (Date.now() - start < timeout - checkInterval) {
|
|
26
|
+
await setTimeout(checkInterval);
|
|
27
|
+
unterminated = getUnterminatedProcesses(childrenPids);
|
|
28
|
+
if (unterminated.length === 0) return;
|
|
29
|
+
}
|
|
30
|
+
kill(unterminated, "SIGKILL");
|
|
43
31
|
}
|
|
44
32
|
async function getChildPids(pid) {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
debug('pstree %s error: %s, ignore it', pid, err);
|
|
53
|
-
}
|
|
54
|
-
return childrenPids;
|
|
33
|
+
let childrenPids = [];
|
|
34
|
+
try {
|
|
35
|
+
childrenPids = (await pstree(pid)).map((c) => parseInt(c.PID));
|
|
36
|
+
} catch (err) {
|
|
37
|
+
debug("pstree %s error: %s, ignore it", pid, err);
|
|
38
|
+
}
|
|
39
|
+
return childrenPids;
|
|
55
40
|
}
|
|
56
41
|
function kill(pids, signal) {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
// ignore
|
|
63
|
-
debug('kill %s error: %s, signal: %s, ignore it', pid, err, signal);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
42
|
+
for (const pid of pids) try {
|
|
43
|
+
process.kill(pid, signal);
|
|
44
|
+
} catch (err) {
|
|
45
|
+
debug("kill %s error: %s, signal: %s, ignore it", pid, err, signal);
|
|
46
|
+
}
|
|
66
47
|
}
|
|
67
48
|
function getUnterminatedProcesses(pids) {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
return false;
|
|
78
|
-
}
|
|
79
|
-
});
|
|
49
|
+
return pids.filter((pid) => {
|
|
50
|
+
try {
|
|
51
|
+
process.kill(pid, 0);
|
|
52
|
+
return true;
|
|
53
|
+
} catch (err) {
|
|
54
|
+
debug("kill %s error: %s, it still alive", pid, err);
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
});
|
|
80
58
|
}
|
|
81
|
-
|
|
59
|
+
|
|
60
|
+
//#endregion
|
|
61
|
+
export { terminate };
|
|
@@ -1,25 +1,29 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
1
|
+
import { BaseAgentWorker } from "./mode/base/agent.js";
|
|
2
|
+
import { BaseAppWorker } from "./mode/base/app.js";
|
|
3
|
+
import { EventEmitter } from "node:events";
|
|
4
|
+
|
|
5
|
+
//#region src/utils/worker_manager.d.ts
|
|
6
|
+
declare class WorkerManager extends EventEmitter {
|
|
7
|
+
agent: BaseAgentWorker | null;
|
|
8
|
+
workers: Map<number, BaseAppWorker>;
|
|
9
|
+
exception: number;
|
|
10
|
+
timer: NodeJS.Timeout;
|
|
11
|
+
constructor();
|
|
12
|
+
getWorkers(): number[];
|
|
13
|
+
setAgent(agent: BaseAgentWorker): void;
|
|
14
|
+
getAgent(): BaseAgentWorker | null;
|
|
15
|
+
deleteAgent(): void;
|
|
16
|
+
setWorker(worker: BaseAppWorker): void;
|
|
17
|
+
getWorker(workerId: number): BaseAppWorker | undefined;
|
|
18
|
+
deleteWorker(workerId: number): void;
|
|
19
|
+
listWorkerIds(): number[];
|
|
20
|
+
listWorkers(): BaseAppWorker[];
|
|
21
|
+
getListeningWorkerIds(): number[];
|
|
22
|
+
count(): {
|
|
23
|
+
agent: number;
|
|
24
|
+
worker: number;
|
|
25
|
+
};
|
|
26
|
+
startCheck(): void;
|
|
25
27
|
}
|
|
28
|
+
//#endregion
|
|
29
|
+
export { WorkerManager };
|