pm2-perfmonitor 1.1.2 → 1.2.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.
- package/README.md +10 -9
- package/lib/app.js +26 -35
- package/lib/defaults.js +35 -0
- package/lib/message.js +22 -0
- package/lib/pm2-extra.js +37 -0
- package/package.json +9 -4
package/README.md
CHANGED
|
@@ -17,15 +17,16 @@ $ pm2 install pm2-perfmonitor
|
|
|
17
17
|
|
|
18
18
|
# Configure
|
|
19
19
|
|
|
20
|
-
| Property | Default Value |
|
|
21
|
-
| :-----------------------------: | :-----------: |
|
|
22
|
-
| `enabled` | `true` |
|
|
23
|
-
| `excludeApps` | - |
|
|
24
|
-
| `includeApps` | - |
|
|
25
|
-
| `workerInterval` | `60000` |
|
|
26
|
-
| `zombieDetection` | `true` |
|
|
27
|
-
| `zombieMaxHits` | `10` |
|
|
28
|
-
| `autoRestartWhenZombieDetected` | `true` |
|
|
20
|
+
| Property | Default Value | Description |
|
|
21
|
+
| :-----------------------------: | :-----------: | :----------------------------------------------------------------------------------: |
|
|
22
|
+
| `enabled` | `true` | Specify whether to enable this module |
|
|
23
|
+
| `excludeApps` | - | Specify the application name that needs to be excluded from guardianship |
|
|
24
|
+
| `includeApps` | - | Specify the application name that needs to be guarded |
|
|
25
|
+
| `workerInterval` | `60000` | Timed task execution interval (ms) |
|
|
26
|
+
| `zombieDetection` | `true` | Specify whether to enable zombie process protection |
|
|
27
|
+
| `zombieMaxHits` | `10` | Specify the maximum occurrence frequency of zombie status |
|
|
28
|
+
| `autoRestartWhenZombieDetected` | `true` | Specify whether to automatically restart zombie processes |
|
|
29
|
+
| `zombieMaxRestarts` | `0` | The maximum number of zombie process restarts can be set to `0` to indicate no limit |
|
|
29
30
|
|
|
30
31
|
# How to set these values ?
|
|
31
32
|
|
package/lib/app.js
CHANGED
|
@@ -1,34 +1,9 @@
|
|
|
1
1
|
const pmx = require('pmx')
|
|
2
2
|
const pm2 = require('pm2')
|
|
3
|
+
const { listAppsAsync } = require('./pm2-extra')
|
|
3
4
|
const { parseParamToArray, parseParamToNumber, parseBool } = require('./utils')
|
|
4
|
-
|
|
5
|
-
const
|
|
6
|
-
enabled: true,
|
|
7
|
-
/**
|
|
8
|
-
* 排除的 app 名
|
|
9
|
-
*/
|
|
10
|
-
excludeApps: [],
|
|
11
|
-
/**
|
|
12
|
-
* 包含的 app 名
|
|
13
|
-
*/
|
|
14
|
-
includeApps: [],
|
|
15
|
-
/**
|
|
16
|
-
* 定时检测间隔(ms)
|
|
17
|
-
*/
|
|
18
|
-
workerInterval: 60000,
|
|
19
|
-
/**
|
|
20
|
-
* 是否开启僵尸进程守护
|
|
21
|
-
*/
|
|
22
|
-
zombieDetection: true,
|
|
23
|
-
/**
|
|
24
|
-
* 僵尸状态最大出现次数
|
|
25
|
-
*/
|
|
26
|
-
zombieMaxHits: 10,
|
|
27
|
-
/**
|
|
28
|
-
* 僵尸状态达到最大容忍度时,是否自动重启僵尸进程
|
|
29
|
-
*/
|
|
30
|
-
autoRestartWhenZombieDetected: true,
|
|
31
|
-
}
|
|
5
|
+
const { defaultOptions } = require('./defaults')
|
|
6
|
+
// const { sendMessage } = require('./message')
|
|
32
7
|
|
|
33
8
|
const conf = pmx.initModule({}, (err, incomingConf) => {
|
|
34
9
|
if (err) {
|
|
@@ -53,6 +28,7 @@ const AUTO_RESTART_WHEN_ZOMBIE_DETECTED = parseBool(
|
|
|
53
28
|
conf.autoRestartWhenZombieDetected,
|
|
54
29
|
)
|
|
55
30
|
const ZOMBIE_MAX_HITS = parseParamToNumber(conf.zombieMaxHits)
|
|
31
|
+
const ZOMBIE_MAX_RESTARTS = parseParamToNumber(conf.zombieMaxRestarts)
|
|
56
32
|
|
|
57
33
|
// 存储每个进程的 CPU 采样历史(pm_id -> [cpu1, cpu2, ...])
|
|
58
34
|
const cpuHistory = new Map()
|
|
@@ -69,6 +45,7 @@ const logger = (type, ...args) => {
|
|
|
69
45
|
|
|
70
46
|
/**
|
|
71
47
|
* 判断是否为僵尸进程:最近 ZOMBIE_MAX_HITS 次全是 0%
|
|
48
|
+
* @param { number[] } history
|
|
72
49
|
*/
|
|
73
50
|
const isZombie = (history) => {
|
|
74
51
|
return history.length >= ZOMBIE_MAX_HITS && history.every((v) => v === 0)
|
|
@@ -77,16 +54,19 @@ const isZombie = (history) => {
|
|
|
77
54
|
/**
|
|
78
55
|
* check zombie process
|
|
79
56
|
*/
|
|
80
|
-
const zombieProcessChecker = () => {
|
|
57
|
+
const zombieProcessChecker = async () => {
|
|
81
58
|
if (!ZOMBIE_DETECTION) return
|
|
82
59
|
|
|
83
|
-
|
|
60
|
+
try {
|
|
61
|
+
const apps = await listAppsAsync()
|
|
62
|
+
|
|
84
63
|
apps.forEach((app) => {
|
|
85
64
|
const { name, pm_id, monit, pm2_env } = app
|
|
86
65
|
|
|
87
66
|
const appStatus = pm2_env?.status
|
|
88
67
|
const appCpuUsage = monit?.cpu || 0
|
|
89
68
|
|
|
69
|
+
// 非目标应用,跳过
|
|
90
70
|
if (
|
|
91
71
|
MODULE_NAME === name ||
|
|
92
72
|
(INCLUDE_APPS.length > 0 && !INCLUDE_APPS.includes(name)) ||
|
|
@@ -95,7 +75,7 @@ const zombieProcessChecker = () => {
|
|
|
95
75
|
return
|
|
96
76
|
}
|
|
97
77
|
|
|
98
|
-
//
|
|
78
|
+
// 只处理 online 状态的进程
|
|
99
79
|
if (appStatus !== 'online') {
|
|
100
80
|
// 进程不在 online 状态时,清空其历史记录,避免干扰
|
|
101
81
|
cpuHistory.delete(pm_id)
|
|
@@ -115,12 +95,21 @@ const zombieProcessChecker = () => {
|
|
|
115
95
|
history.shift()
|
|
116
96
|
}
|
|
117
97
|
|
|
118
|
-
//
|
|
119
|
-
|
|
98
|
+
// 判断是否为僵尸:最近 ZOMBIE_MAX_HITS 次全是 0%
|
|
120
99
|
if (isZombie(history)) {
|
|
121
|
-
logger(
|
|
100
|
+
logger(
|
|
101
|
+
'info',
|
|
102
|
+
`Zombie detected: ${name} (pm_id: ${pm_id}, pid: ${app.pid})`,
|
|
103
|
+
)
|
|
122
104
|
|
|
123
105
|
if (AUTO_RESTART_WHEN_ZOMBIE_DETECTED) {
|
|
106
|
+
if (
|
|
107
|
+
ZOMBIE_MAX_RESTARTS > 0 &&
|
|
108
|
+
zombieRestartHistory.get(pm_id) >= ZOMBIE_MAX_RESTARTS
|
|
109
|
+
) {
|
|
110
|
+
return
|
|
111
|
+
}
|
|
112
|
+
|
|
124
113
|
logger('info', 'restarting...')
|
|
125
114
|
|
|
126
115
|
pm2.restart(pm_id, (restartErr) => {
|
|
@@ -162,7 +151,9 @@ const zombieProcessChecker = () => {
|
|
|
162
151
|
}
|
|
163
152
|
}
|
|
164
153
|
})
|
|
165
|
-
})
|
|
154
|
+
} catch (err) {
|
|
155
|
+
logger('error', err)
|
|
156
|
+
}
|
|
166
157
|
}
|
|
167
158
|
|
|
168
159
|
const runModule = () => {
|
package/lib/defaults.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
const defaultOptions = {
|
|
2
|
+
enabled: true,
|
|
3
|
+
/**
|
|
4
|
+
* 排除的 app 名
|
|
5
|
+
*/
|
|
6
|
+
excludeApps: [],
|
|
7
|
+
/**
|
|
8
|
+
* 包含的 app 名
|
|
9
|
+
*/
|
|
10
|
+
includeApps: [],
|
|
11
|
+
/**
|
|
12
|
+
* 定时检测间隔(ms)
|
|
13
|
+
*/
|
|
14
|
+
workerInterval: 60000,
|
|
15
|
+
/**
|
|
16
|
+
* 是否开启僵尸进程守护
|
|
17
|
+
*/
|
|
18
|
+
zombieDetection: true,
|
|
19
|
+
/**
|
|
20
|
+
* 僵尸状态最大出现次数
|
|
21
|
+
*/
|
|
22
|
+
zombieMaxHits: 10,
|
|
23
|
+
/**
|
|
24
|
+
* 僵尸状态达到最大容忍度时,是否自动重启僵尸进程
|
|
25
|
+
*/
|
|
26
|
+
autoRestartWhenZombieDetected: true,
|
|
27
|
+
/**
|
|
28
|
+
* 僵尸进程最大重启次数,设置为0表示不限制
|
|
29
|
+
*/
|
|
30
|
+
zombieMaxRestarts: 0,
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
module.exports = {
|
|
34
|
+
defaultOptions,
|
|
35
|
+
}
|
package/lib/message.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const pm2 = require('pm2')
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @param { number } pid 进程id
|
|
5
|
+
* @param { string } eventName 事件名
|
|
6
|
+
* @param { Object } data
|
|
7
|
+
*/
|
|
8
|
+
const sendMessage = (pid, eventName, data) => {
|
|
9
|
+
pm2.sendDataToProcessId(pid, {
|
|
10
|
+
id: pid,
|
|
11
|
+
type: 'process:msg',
|
|
12
|
+
topic: true,
|
|
13
|
+
data: {
|
|
14
|
+
event: `pm2-perfmonitor:${eventName}`,
|
|
15
|
+
data,
|
|
16
|
+
},
|
|
17
|
+
})
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
module.exports = {
|
|
21
|
+
sendMessage,
|
|
22
|
+
}
|
package/lib/pm2-extra.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
const pm2 = require('pm2')
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @returns { Promise<pm2.ProcessDescription[]> }
|
|
5
|
+
*/
|
|
6
|
+
const listAppsAsync = () => {
|
|
7
|
+
return new Promise((resolve, reject) => {
|
|
8
|
+
pm2.list((err, apps) => {
|
|
9
|
+
if (err) {
|
|
10
|
+
return reject(err)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
resolve(apps)
|
|
14
|
+
})
|
|
15
|
+
})
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @param { string | number} pm_id
|
|
20
|
+
* @returns { Promise<void> }
|
|
21
|
+
*/
|
|
22
|
+
const stopAppAsync = (pm_id) => {
|
|
23
|
+
return new Promise((resolve, reject) => {
|
|
24
|
+
pm2.stop(pm_id, (err) => {
|
|
25
|
+
if (err) {
|
|
26
|
+
return reject(err)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
resolve()
|
|
30
|
+
})
|
|
31
|
+
})
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
module.exports = {
|
|
35
|
+
listAppsAsync,
|
|
36
|
+
stopAppAsync,
|
|
37
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pm2-perfmonitor",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.3",
|
|
4
4
|
"description": "A pm2 module for performance monitoring. Automatically detect zombie processes and restart it",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "elenh",
|
|
@@ -20,8 +20,12 @@
|
|
|
20
20
|
},
|
|
21
21
|
"homepage": "https://github.com/yisibell/pm2-perfmonitor",
|
|
22
22
|
"scripts": {
|
|
23
|
-
"start": "pm2
|
|
24
|
-
"
|
|
23
|
+
"start-or-restart:app": "pm2 startOrRestart ecosystem.app.config.cjs --update-env",
|
|
24
|
+
"start:app": "pm2 start ecosystem.app.config.cjs",
|
|
25
|
+
"restart:app": "pm2 restart ecosystem.app.config.cjs",
|
|
26
|
+
"delete-start:app": "pm2 delete app1 || true && pm2 start ecosystem.app.config.cjs",
|
|
27
|
+
"start": "node ./scripts/app.js --env=app",
|
|
28
|
+
"dev": "pm2 restart ecosystem.dev.config.cjs",
|
|
25
29
|
"release": "changelogen --release && npm publish --access=public && git push --follow-tags"
|
|
26
30
|
},
|
|
27
31
|
"keywords": [
|
|
@@ -43,6 +47,7 @@
|
|
|
43
47
|
},
|
|
44
48
|
"devDependencies": {
|
|
45
49
|
"changelogen": "^0.6.2",
|
|
46
|
-
"cz-conventional-changelog": "^3.3.0"
|
|
50
|
+
"cz-conventional-changelog": "^3.3.0",
|
|
51
|
+
"minimist": "^1.2.8"
|
|
47
52
|
}
|
|
48
53
|
}
|