topbit 3.0.7 → 3.1.0
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.cn.md +1 -1
- package/README.md +1 -1
- package/bin/new-ctl.js +15 -50
- package/demo/allow.js +28 -8
- package/demo/monitor.js +78 -0
- package/docs/en/topbit-token.md +7 -4
- package/docs/topbit-token.md +10 -4
- package/package.json +1 -1
- package/src/monitor.js +102 -90
- package/src/topbit.js +8 -8
package/README.cn.md
CHANGED
package/README.md
CHANGED
|
@@ -879,7 +879,7 @@ Full configuration options for app initialization are as follows. Please read th
|
|
|
879
879
|
// Display load info, requires enabling cluster mode via daemon interface
|
|
880
880
|
loadMonitor : true,
|
|
881
881
|
|
|
882
|
-
// Load info type: text,
|
|
882
|
+
// Load info type: text, null, obj, orgobj
|
|
883
883
|
// json type is for program communication, convenient for interface development
|
|
884
884
|
loadInfoType : 'text',
|
|
885
885
|
|
package/bin/new-ctl.js
CHANGED
|
@@ -4,66 +4,33 @@
|
|
|
4
4
|
|
|
5
5
|
let simple_mode = false
|
|
6
6
|
|
|
7
|
-
let type_context =
|
|
7
|
+
let type_context = `
|
|
8
8
|
/**
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* @typedef {object} ext
|
|
12
|
-
* @property {function} extName(filename: string) - 解析文件扩展名。
|
|
13
|
-
* @property {function} makeName(filename: string) - 生成唯一文件名,默认为时间字符串+随机数:2021-01-01_12-12-23_27789.jpg,filename主要用于获取扩展名。
|
|
14
|
-
* @property {function} nrand(n, m) - 返回两个数之间的随机数。
|
|
15
|
-
* @property {function} uuid() - 返回唯一字符串用于唯一ID,支持标准的8-4-4-4-12和短字符串8-2-2-4两种模式(传递参数为true)。
|
|
16
|
-
* @property {function} timestr() - 默认返回年-月-日_时-分-秒,没有空格,方便保存和解析。
|
|
17
|
-
* @property {function} sha1(text, encoding = 'hex')
|
|
18
|
-
* @property {function} sha256(text, encoding = 'hex')
|
|
19
|
-
* @property {function} sha512(text, encoding = 'hex')
|
|
20
|
-
* @property {function} sm3(text, encoding = 'hex')
|
|
21
|
-
* @property {function} hmacsha1(text, key, encoding = 'hex')
|
|
22
|
-
* @property {function} pipe(filename: string, reply: object)
|
|
23
|
-
*
|
|
24
|
-
* 更多参考:{@link https://gitee.com/daoio/titbit/wikis/helper-function}
|
|
25
|
-
*/
|
|
26
|
-
/**
|
|
27
|
-
* --------------------------------- 请求上下文 -----------------------------------------
|
|
9
|
+
* ----------------------------- 请求上下文 -----------------------------------
|
|
28
10
|
*
|
|
29
11
|
* @typedef {object} context
|
|
30
12
|
* @property {string} version - 协议版本,字符串类型,为'1.1' 或 '2' 或 '3'。
|
|
31
13
|
* @property {number} major - 协议主要版本号,1、2、3分别表示HTTP/1.1 HTTP/2 HTTP/3。
|
|
32
14
|
* @property {string} method - 请求类型,GET POST等HTTP请求类型,大写字母的字符串。
|
|
33
15
|
* @property {string} path - 具体请求的路径,比如 /x/y。
|
|
34
|
-
* @property {string} routepath -
|
|
35
|
-
* @property {boolean} isUpload - 是否为上传文件请求,此时就是检测消息头content-type是否为multipart/form-data格式。
|
|
16
|
+
* @property {string} routepath - 实际执行请求的路由字符串。
|
|
36
17
|
* @property {object} box - 默认为空对象,可以添加任何属性值,用来动态传递给下一层组件需要使用的信息。
|
|
37
18
|
* @property {object} query - url传递的参数。
|
|
38
19
|
* @property {object} param - 路由参数。
|
|
39
|
-
* @property {object} headers - 指向
|
|
40
|
-
* @property {object}
|
|
41
|
-
* @property {
|
|
42
|
-
* @property {object}
|
|
43
|
-
* @property {object} reply - HTTP/1.1协议,指向response,HTTP/2 指向stream。
|
|
20
|
+
* @property {object} headers - 指向req.headers。
|
|
21
|
+
* @property {(object|string|buffer)} body - body请求体的数据,具体格式需要看content-type,一般为字符串或者对象,也可能是buffer。
|
|
22
|
+
* @property {object} req HTTP/1.1 - 就是http模块request事件的参数IncomingMessage对象,HTTP/2 指向stream对象。
|
|
23
|
+
* @property {object} res - HTTP/1.1协议,指向response,HTTP/2 指向stream。
|
|
44
24
|
* @property {object} service - 用于依赖注入的对象,指向app.service。
|
|
45
|
-
* @property {function} moveFile(file: object) -
|
|
46
|
-
* @property {function} getFile(
|
|
47
|
-
* @property {function} sendHeader() - 发送消息头,针对http/2设计,http/1.1只是一个空函数,为了代码保持一致。
|
|
25
|
+
* @property {function} moveFile(file: object) - 用来移动上传的文件到指定路径。
|
|
26
|
+
* @property {function} getFile() - 根据上传名获取上传的文件。
|
|
48
27
|
* @property {function} setHeader(key: string, value: string|array)
|
|
49
|
-
* @property {function}
|
|
28
|
+
* @property {function} to(body: string|object|buffer) - 设置要返回的body数据。
|
|
50
29
|
* @property {function} status(code: null | number) - 设置状态码,默认为null表示返回状态码。
|
|
51
|
-
* @property {ext} ext - 指向ext模块,提供了一些助手函数,具体参考wiki。
|
|
52
|
-
*
|
|
53
|
-
* 更多参考:{@link https://gitee.com/daoio/titbit/blob/master/README.md#%E8%AF%B7%E6%B1%82%E4%B8%8A%E4%B8%8B%E6%96%87}
|
|
54
30
|
*/
|
|
55
31
|
`
|
|
56
32
|
|
|
57
|
-
let head_hint =
|
|
58
|
-
提示:
|
|
59
|
-
表单提交或异步请求,对应于POST或PUT请求,对应函数post和put,提交的请求体数据通过 ctx.body 获取。
|
|
60
|
-
|
|
61
|
-
路由参数通过 ctx.param 获取,示例:let id = ctx.param.id
|
|
62
|
-
|
|
63
|
-
url参数(?a=1&b=2)通过 ctx.query 获取,示例:let name = ctx.query.name
|
|
64
|
-
|
|
65
|
-
使用ctx.getFile(name)获取上传的文件,示例:let f = ctx.getFile('image')
|
|
66
|
-
**********************************************************************/\n\n`
|
|
33
|
+
let head_hint = ''
|
|
67
34
|
|
|
68
35
|
function fmt_ctx_param(text) {
|
|
69
36
|
let ctx_param = `
|
|
@@ -88,16 +55,14 @@ function makeController(name) {
|
|
|
88
55
|
+ (simple_mode ? '' : head_hint)
|
|
89
56
|
+ `class ${modname} {\n\n`
|
|
90
57
|
+ ` constructor() {\n`
|
|
91
|
-
+ `
|
|
92
|
-
+ ` //若要改变路由,则可以设置此属性,比如设置为/:name\n`
|
|
58
|
+
+ ` //若要改变路由,则设置此属性,比如设置为/:name\n`
|
|
93
59
|
+ ` //this.param = '/:id'\n`
|
|
94
60
|
+ ` }\n\n`
|
|
95
|
-
+ `
|
|
96
|
-
+ ` //service默认是app.service,此参数通过titbit-loader初始化的initArgs选项进行控制。\n`
|
|
61
|
+
+ ` //加载器初始化后会执行此函数。service默认是app.service。\n`
|
|
97
62
|
+ ` async init(service) {\n`
|
|
98
63
|
+ ` \n`
|
|
99
64
|
+ ` }\n\n`
|
|
100
|
-
+ (simple_mode ? '' : `
|
|
65
|
+
+ (simple_mode ? '' : ` // 根据实际需要增加或删除请求方法。\n`)
|
|
101
66
|
+ `${fmt_ctx_param('获取资源具体内容')}`
|
|
102
67
|
+ ` async get(ctx) {\n\n`
|
|
103
68
|
+ ` }\n`
|
|
@@ -112,7 +77,7 @@ function makeController(name) {
|
|
|
112
77
|
+ ` }\n`
|
|
113
78
|
+ `${fmt_ctx_param('删除资源')}`
|
|
114
79
|
+ ` async _delete(ctx) {\n\n`
|
|
115
|
-
+ ` }\n`
|
|
80
|
+
+ ` }\n\n`
|
|
116
81
|
+ `}\n\nmodule.exports = ${modname}\n`
|
|
117
82
|
+ (simple_mode ? '' : `${type_context}`)
|
|
118
83
|
|
package/demo/allow.js
CHANGED
|
@@ -1,8 +1,21 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
const Topbit = require('../src/topbit.js');
|
|
4
|
+
const npargv = Topbit.npargv
|
|
5
|
+
|
|
6
|
+
let {args} = npargv({
|
|
7
|
+
'--loadtype': {
|
|
8
|
+
name: 'load',
|
|
9
|
+
default: 'text',
|
|
10
|
+
limit: ['text', 'json', 'orgjson']
|
|
11
|
+
},
|
|
12
|
+
'--loadstdio': {
|
|
13
|
+
name: 'loadstdio',
|
|
14
|
+
default: false
|
|
15
|
+
}
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
const app = new Topbit({
|
|
6
19
|
debug : true,
|
|
7
20
|
allow : new Set(['127.0.0.1']),
|
|
8
21
|
maxIPRequest: 2,
|
|
@@ -10,11 +23,11 @@ const app = new titbit({
|
|
|
10
23
|
useLimit: true,
|
|
11
24
|
maxConn: 2000,
|
|
12
25
|
//http2: true,
|
|
13
|
-
|
|
14
|
-
loadInfoType :
|
|
26
|
+
loadMonitor: true,
|
|
27
|
+
loadInfoType : args.load,
|
|
15
28
|
globalLog : true,
|
|
16
29
|
logType: 'stdio',
|
|
17
|
-
loadInfoFile : '/tmp/loadinfo.log'
|
|
30
|
+
loadInfoFile : args.loadstdio ? '' : '/tmp/loadinfo.log',
|
|
18
31
|
});
|
|
19
32
|
|
|
20
33
|
app.use(async (c, next) => {
|
|
@@ -65,7 +78,12 @@ app.get('/', async c => {
|
|
|
65
78
|
}, 'home');
|
|
66
79
|
|
|
67
80
|
app.get('/test', async c => {
|
|
68
|
-
c.
|
|
81
|
+
//await c.ext.delay(10)
|
|
82
|
+
let sum = 0
|
|
83
|
+
for (let i = 0; i < 1000000; i++) {
|
|
84
|
+
sum += Math.random() * i;
|
|
85
|
+
}
|
|
86
|
+
c.to({sum});
|
|
69
87
|
}, {group: 'test', name : 'test'});
|
|
70
88
|
|
|
71
89
|
app.post('/test', async c => {
|
|
@@ -95,4 +113,6 @@ app.use(async (c, next) => {
|
|
|
95
113
|
}, {pre: true, method: 'POST', name: 'transmit'});
|
|
96
114
|
|
|
97
115
|
|
|
98
|
-
app.
|
|
116
|
+
app.autoWorker(3)
|
|
117
|
+
|
|
118
|
+
app.daemon(2034, 1)
|
package/demo/monitor.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const Topbit = require('../src/topbit.js');
|
|
4
|
+
const npargv = Topbit.npargv
|
|
5
|
+
|
|
6
|
+
let {args} = npargv({
|
|
7
|
+
'--loadtype': {
|
|
8
|
+
name: 'loadtype',
|
|
9
|
+
default: 'text',
|
|
10
|
+
limit: ['text', 'json', 'orgjson', 'obj', 'orgobj']
|
|
11
|
+
},
|
|
12
|
+
'--loadstdio': {
|
|
13
|
+
name: 'loadstdio',
|
|
14
|
+
default: false
|
|
15
|
+
}
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
console.log(args)
|
|
19
|
+
|
|
20
|
+
const app = new Topbit({
|
|
21
|
+
debug : true,
|
|
22
|
+
allow : new Set(['127.0.0.1']),
|
|
23
|
+
maxIPRequest: 2,
|
|
24
|
+
unitTime: 10,
|
|
25
|
+
useLimit: true,
|
|
26
|
+
maxConn: 2000,
|
|
27
|
+
//http2: true,
|
|
28
|
+
loadMonitor: true,
|
|
29
|
+
loadInfoType : args.loadtype,
|
|
30
|
+
globalLog : false,
|
|
31
|
+
logType: 'stdio',
|
|
32
|
+
loadInfoFile : args.loadstdio ? '' : '/tmp/topbit-loadinfo.log',
|
|
33
|
+
maxLoadRate: 0.56
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
app.get('/', async c => {
|
|
37
|
+
c.to('ok');
|
|
38
|
+
}, 'home');
|
|
39
|
+
|
|
40
|
+
app.get('/test', async c => {
|
|
41
|
+
//await c.ext.delay(10)
|
|
42
|
+
let sum = 0
|
|
43
|
+
for (let i = 0; i < 90000; i++) {
|
|
44
|
+
sum += Math.random() * i;
|
|
45
|
+
}
|
|
46
|
+
c.to({sum});
|
|
47
|
+
}, {group: 'test', name : 'test'});
|
|
48
|
+
|
|
49
|
+
app.post('/test', async c => {
|
|
50
|
+
c.to(c.body);
|
|
51
|
+
}, {group: 'test', name : 'test-post'});
|
|
52
|
+
|
|
53
|
+
app.post('/transmit', async c => {
|
|
54
|
+
c.to('ok');
|
|
55
|
+
}, 'transmit');
|
|
56
|
+
|
|
57
|
+
app.use(async (c, next) => {
|
|
58
|
+
let total = 0;
|
|
59
|
+
|
|
60
|
+
c.box.dataHandle = (data) => {
|
|
61
|
+
total += data.length;
|
|
62
|
+
if (total > 32) {
|
|
63
|
+
c.response.statusCode = 413;
|
|
64
|
+
c.response.end('太多了,限制32字节以内');
|
|
65
|
+
return ;
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
await next(c);
|
|
70
|
+
|
|
71
|
+
console.log(total, 'bytes');
|
|
72
|
+
|
|
73
|
+
}, {pre: true, method: 'POST', name: 'transmit'});
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
app.autoWorker(4)
|
|
77
|
+
|
|
78
|
+
app.daemon(2034, 1)
|
package/docs/en/topbit-token.md
CHANGED
|
@@ -35,8 +35,8 @@ Built entirely on Node.js native `crypto`, supports:
|
|
|
35
35
|
### 3. 30-Second Quick Start
|
|
36
36
|
|
|
37
37
|
```js
|
|
38
|
-
|
|
39
|
-
const TopbitToken =
|
|
38
|
+
const Topbit = require('topbit')
|
|
39
|
+
const TopbitToken = Topbit.Token
|
|
40
40
|
|
|
41
41
|
const token = new TopbitToken({
|
|
42
42
|
key : 'your-very-strong-32-byte-secret!!',
|
|
@@ -79,6 +79,9 @@ async post(c) {
|
|
|
79
79
|
### 5. Advanced: Multi-Key + Instant Revocation
|
|
80
80
|
|
|
81
81
|
```js
|
|
82
|
+
const Topbit = require('topbit')
|
|
83
|
+
const TopbitToken = Topbit.Token
|
|
84
|
+
|
|
82
85
|
const token = new TopbitToken({ key: 'current-master-key' })
|
|
83
86
|
|
|
84
87
|
// Add multiple key versions
|
|
@@ -112,8 +115,8 @@ Frontend just replaces the old one → seamless “never expire” experience.
|
|
|
112
115
|
### 7. Production Recommended Setup
|
|
113
116
|
|
|
114
117
|
```js
|
|
115
|
-
|
|
116
|
-
const TopbitToken =
|
|
118
|
+
const Topbit = require('topbit')
|
|
119
|
+
const TopbitToken = Topbit.Token
|
|
117
120
|
|
|
118
121
|
const token = new TopbitToken({
|
|
119
122
|
algorithm : 'aes-256-gcm',
|
package/docs/topbit-token.md
CHANGED
|
@@ -36,8 +36,8 @@ TopbitToken 是专为 Topbit 框架打造的零依赖、极简、高安全的加
|
|
|
36
36
|
### 三、快速上手(30 秒搞定登录认证)
|
|
37
37
|
|
|
38
38
|
```js
|
|
39
|
-
|
|
40
|
-
const TopbitToken =
|
|
39
|
+
const Topbit = require('topbit')
|
|
40
|
+
const TopbitToken = Topbit.Token
|
|
41
41
|
|
|
42
42
|
const token = new TopbitToken({
|
|
43
43
|
key : 'your-32-byte-secret-key-here!!', // 必须 32 字节(AES-256)
|
|
@@ -89,6 +89,9 @@ token.mid() 会自动把验证后的用户信息挂到 c.user
|
|
|
89
89
|
### 五、高级功能:多密钥 + 即时失效(防泄漏神器)
|
|
90
90
|
|
|
91
91
|
```js
|
|
92
|
+
const Topbit = require('topbit')
|
|
93
|
+
const TopbitToken = Topbit.Token
|
|
94
|
+
|
|
92
95
|
const token = new TopbitToken({
|
|
93
96
|
key: 'master-key-2025-01-01',
|
|
94
97
|
expires: 3600 * 24 * 30
|
|
@@ -113,6 +116,9 @@ token.tokenIds = []
|
|
|
113
116
|
### 六、自动刷新 Token 机制
|
|
114
117
|
|
|
115
118
|
```js
|
|
119
|
+
const Topbit = require('topbit')
|
|
120
|
+
const TopbitToken = Topbit.Token
|
|
121
|
+
|
|
116
122
|
const token = new TopbitToken({
|
|
117
123
|
expires: 3600 * 24, // 24小时有效
|
|
118
124
|
refresh: true // 开启自动刷新
|
|
@@ -128,8 +134,8 @@ const token = new TopbitToken({
|
|
|
128
134
|
### 七、最佳实践(生产级推荐配置)
|
|
129
135
|
|
|
130
136
|
```js
|
|
131
|
-
|
|
132
|
-
const TopbitToken =
|
|
137
|
+
const Topbit = require('topbit')
|
|
138
|
+
const TopbitToken = Topbit.Token
|
|
133
139
|
|
|
134
140
|
const token = new TopbitToken({
|
|
135
141
|
algorithm : 'aes-256-gcm',
|
package/package.json
CHANGED
package/src/monitor.js
CHANGED
|
@@ -5,6 +5,8 @@ const os = require('node:os');
|
|
|
5
5
|
const fs = require('node:fs');
|
|
6
6
|
const process = require('node:process');
|
|
7
7
|
|
|
8
|
+
const hrtime = process.hrtime
|
|
9
|
+
|
|
8
10
|
class Monitor {
|
|
9
11
|
|
|
10
12
|
constructor(options) {
|
|
@@ -15,6 +17,8 @@ class Monitor {
|
|
|
15
17
|
|
|
16
18
|
this.workerCount = options.workerCount
|
|
17
19
|
|
|
20
|
+
this.isLoadObj = ['obj', 'orgobj'].includes(this.config.loadInfoType)
|
|
21
|
+
|
|
18
22
|
this.rundata.cpus = os.cpus().length
|
|
19
23
|
|
|
20
24
|
this.loadCount = 0
|
|
@@ -24,9 +28,9 @@ class Monitor {
|
|
|
24
28
|
|
|
25
29
|
this.loadCache = null
|
|
26
30
|
|
|
27
|
-
this.cpuLastTime =
|
|
31
|
+
this.cpuLastTime = hrtime.bigint() - 1n
|
|
28
32
|
|
|
29
|
-
this.cpuNowTime =
|
|
33
|
+
this.cpuNowTime = hrtime.bigint()
|
|
30
34
|
|
|
31
35
|
//this.cpuPercentFactor = 10 * this.rundata.cpus;
|
|
32
36
|
this.cpuHighRatio = 0
|
|
@@ -45,10 +49,36 @@ class Monitor {
|
|
|
45
49
|
|
|
46
50
|
this.life = 20
|
|
47
51
|
|
|
48
|
-
this.maxRate =
|
|
52
|
+
this.maxRate = this.config.maxLoadRate
|
|
49
53
|
|
|
50
54
|
this.loadfd = null
|
|
51
55
|
|
|
56
|
+
this.ipcMsgCache = {
|
|
57
|
+
type: '_load',
|
|
58
|
+
pid: 0,
|
|
59
|
+
cpu: null,
|
|
60
|
+
cputm: 0,
|
|
61
|
+
mem: null,
|
|
62
|
+
conn: 0
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
this.loadjson = {
|
|
66
|
+
masterPid : process.pid,
|
|
67
|
+
listen : `${this.rundata.host}:${this.rundata.port}`,
|
|
68
|
+
CPULoadavg : {
|
|
69
|
+
'1m' : '0',
|
|
70
|
+
'5m' : '0',
|
|
71
|
+
'15m' : '0'
|
|
72
|
+
},
|
|
73
|
+
https: this.config.https,
|
|
74
|
+
http2: this.config.http2,
|
|
75
|
+
workers : []
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
queueMicrotask(() => {
|
|
79
|
+
this.loadjson.listen = `${this.rundata.host}:${this.rundata.port}`
|
|
80
|
+
})
|
|
81
|
+
|
|
52
82
|
}
|
|
53
83
|
|
|
54
84
|
autoWorker() {
|
|
@@ -65,20 +95,20 @@ class Monitor {
|
|
|
65
95
|
for (let k in this.workers) {
|
|
66
96
|
cpuratio = (this.workers[k].cpu.user + this.workers[k].cpu.system) / this.workers[k].cputm
|
|
67
97
|
|
|
68
|
-
if (cpuratio
|
|
69
|
-
this.cpuHighRatio
|
|
98
|
+
if (cpuratio > this.maxRate) {
|
|
99
|
+
this.cpuHighRatio++
|
|
70
100
|
} else {
|
|
71
101
|
if (this.cpuHighRatio > 0) {
|
|
72
|
-
this.cpuHighRatio
|
|
102
|
+
this.cpuHighRatio--
|
|
73
103
|
}
|
|
104
|
+
|
|
74
105
|
break
|
|
75
106
|
}
|
|
76
107
|
}
|
|
77
108
|
}
|
|
78
109
|
|
|
79
110
|
if (this.cpuHighRatio >= this.workerCount.cur) {
|
|
80
|
-
|
|
81
|
-
this.cpuHighRatio -= 1
|
|
111
|
+
this.cpuHighRatio--
|
|
82
112
|
|
|
83
113
|
if (this.workerCount.cur < this.workerCount.max) {
|
|
84
114
|
if (this.workerCount.canAutoFork) {
|
|
@@ -106,7 +136,7 @@ class Monitor {
|
|
|
106
136
|
|
|
107
137
|
if (this.workerCount.cur > this.workerCount.total) {
|
|
108
138
|
if (this.cooling > 0) {
|
|
109
|
-
this.cooling
|
|
139
|
+
this.cooling--
|
|
110
140
|
} else {
|
|
111
141
|
for (let k in this.workers) {
|
|
112
142
|
if (this.workers[k].conn === 0) {
|
|
@@ -169,12 +199,16 @@ class Monitor {
|
|
|
169
199
|
if (will_disconnect) return;
|
|
170
200
|
|
|
171
201
|
this.cpuLastTime = this.cpuNowTime
|
|
172
|
-
this.cpuNowTime =
|
|
202
|
+
this.cpuNowTime = hrtime.bigint()
|
|
203
|
+
let diffNs = this.cpuNowTime - this.cpuLastTime
|
|
204
|
+
let diffUs = (Number(diffNs) / 1000) + 0.01
|
|
173
205
|
|
|
206
|
+
//此处是计算微秒的cpu变化,而计算负载正好按照微秒进行
|
|
174
207
|
this.rundata.cpuTime = process.cpuUsage(this.rundata.cpuLast)
|
|
208
|
+
|
|
175
209
|
if (mem_count < MAX_MEM_COUNT) {
|
|
176
210
|
this.rundata.mem.rss = process.memoryUsage.rss()
|
|
177
|
-
mem_count
|
|
211
|
+
mem_count++
|
|
178
212
|
} else {
|
|
179
213
|
this.rundata.mem = process.memoryUsage()
|
|
180
214
|
mem_count = 0
|
|
@@ -182,16 +216,17 @@ class Monitor {
|
|
|
182
216
|
|
|
183
217
|
this.rundata.mem.total = this.rundata.mem.rss + this.rundata.mem.external
|
|
184
218
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
err
|
|
194
|
-
//
|
|
219
|
+
const msg = this.ipcMsgCache
|
|
220
|
+
msg.pid = process.pid
|
|
221
|
+
msg.cpu = this.rundata.cpuTime
|
|
222
|
+
msg.cputm = diffUs
|
|
223
|
+
msg.mem = this.rundata.mem
|
|
224
|
+
msg.conn = this.rundata.conn
|
|
225
|
+
|
|
226
|
+
process.send(msg, err => {
|
|
227
|
+
if (err) this.config.errorHandle(err, '--ERR-WORKER-SEND--')
|
|
228
|
+
// 忽略管道关闭错误
|
|
229
|
+
//if (err && err.code !== 'ERR_IPC_CHANNEL_CLOSED') {}
|
|
195
230
|
})
|
|
196
231
|
|
|
197
232
|
this.rundata.cpuLast = process.cpuUsage()
|
|
@@ -201,7 +236,7 @@ class Monitor {
|
|
|
201
236
|
}
|
|
202
237
|
|
|
203
238
|
showLoadInfo(w, id) {
|
|
204
|
-
if (this.config.loadInfoType
|
|
239
|
+
if (!this.config.loadInfoType || this.config.loadInfoType === 'null') {
|
|
205
240
|
return
|
|
206
241
|
}
|
|
207
242
|
|
|
@@ -225,31 +260,30 @@ class Monitor {
|
|
|
225
260
|
|
|
226
261
|
this.loadCount += 1;
|
|
227
262
|
if (this.loadCount < this.workerCount.cur) {
|
|
228
|
-
return
|
|
263
|
+
return
|
|
229
264
|
}
|
|
230
265
|
|
|
231
|
-
let
|
|
266
|
+
let load_info = this.fmtLoadInfo(this.config.loadInfoType);
|
|
232
267
|
|
|
233
268
|
if (this.config.loadInfoFile === '--mem') {
|
|
234
|
-
this.loadCache =
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
269
|
+
this.loadCache = load_info;
|
|
270
|
+
} else if (this.loadfd !== null) {
|
|
271
|
+
fs.write(this.loadfd, this.isLoadObj ? JSON.stringify(load_info) : load_info, 0,
|
|
272
|
+
(err, bytes, data) => {
|
|
273
|
+
if (err && this.config.debug) this.config.errorHandle(err, '--ERR-WRITE-FILE--');
|
|
274
|
+
|
|
275
|
+
if (!err) {
|
|
276
|
+
fs.ftruncate(this.loadfd, bytes, e => {});
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
);
|
|
245
280
|
} else if (process.ppid > 1 && !this.config.daemon && !this.config.loadInfoFile) {
|
|
246
281
|
console.clear();
|
|
247
282
|
//只有没有开启守护进程才会输出到屏幕
|
|
248
|
-
console.log(
|
|
283
|
+
console.log(load_info);
|
|
249
284
|
}
|
|
250
285
|
|
|
251
286
|
this.loadCount = 0;
|
|
252
|
-
|
|
253
287
|
}
|
|
254
288
|
|
|
255
289
|
checkMem(w, msg) {
|
|
@@ -292,26 +326,16 @@ class Monitor {
|
|
|
292
326
|
let p = null;
|
|
293
327
|
|
|
294
328
|
for (let id in this.workers) {
|
|
295
|
-
|
|
296
329
|
p = this.workers[id];
|
|
297
|
-
|
|
298
330
|
tmp = [(`${p.pid}`).padEnd(9, ' ')];
|
|
299
|
-
|
|
300
331
|
t = p.cpu.user + p.cpu.system;
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
(p.mem.heapTotal / 1048576).toFixed(1).padEnd(8, ' '),
|
|
309
|
-
|
|
310
|
-
(p.mem.heapUsed / 1048576).toFixed(1).padEnd(8, ' '),
|
|
311
|
-
|
|
312
|
-
(p.mem.external / 1048576).toFixed(1).padEnd(9, ' '),
|
|
313
|
-
|
|
314
|
-
(p.mem.total / 1048576).toFixed(1)
|
|
332
|
+
tmp.push((( t * 100 / p.cputm ).toFixed(2) + '%').padEnd(8, ' '),
|
|
333
|
+
(`${p.conn}`).padEnd(8, ' '),
|
|
334
|
+
(p.mem.rss / 1048576).toFixed(1).padEnd(8, ' '),
|
|
335
|
+
(p.mem.heapTotal / 1048576).toFixed(1).padEnd(8, ' '),
|
|
336
|
+
(p.mem.heapUsed / 1048576).toFixed(1).padEnd(8, ' '),
|
|
337
|
+
(p.mem.external / 1048576).toFixed(1).padEnd(9, ' '),
|
|
338
|
+
(p.mem.total / 1048576).toFixed(1)
|
|
315
339
|
);
|
|
316
340
|
|
|
317
341
|
cols.push(tmp.join(''));
|
|
@@ -323,56 +347,44 @@ class Monitor {
|
|
|
323
347
|
+`HTTPS: ${this.config.https ? 'true' : 'false'}; HTTP/2: ${this.config.http2 ? 'true' : 'false'}\n`;
|
|
324
348
|
}
|
|
325
349
|
|
|
326
|
-
if (type === '
|
|
327
|
-
let
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
'15m' : oavg[2].toFixed(2)
|
|
334
|
-
},
|
|
335
|
-
https: this.config.https,
|
|
336
|
-
http2: this.config.http2,
|
|
337
|
-
workers : []
|
|
338
|
-
};
|
|
350
|
+
if (type === 'obj') {
|
|
351
|
+
let lj = this.loadjson
|
|
352
|
+
lj.CPULoadavg['1m'] = oavg[0].toFixed(2)
|
|
353
|
+
lj.CPULoadavg['5m'] = oavg[1].toFixed(2)
|
|
354
|
+
lj.CPULoadavg['15m'] = oavg[2].toFixed(2)
|
|
355
|
+
lj.workers = []
|
|
356
|
+
|
|
339
357
|
for (let id in this.workers) {
|
|
340
358
|
p = this.workers[id];
|
|
341
359
|
|
|
342
|
-
|
|
343
|
-
pid
|
|
344
|
-
cpu
|
|
345
|
-
cputm
|
|
346
|
-
mem
|
|
360
|
+
lj.workers.push({
|
|
361
|
+
pid: p.pid,
|
|
362
|
+
cpu: `${((p.cpu.user + p.cpu.system) * 100 / p.cputm).toFixed(2)}%`,
|
|
363
|
+
cputm: p.cputm,
|
|
364
|
+
mem: {
|
|
347
365
|
rss : (p.mem.rss / 1048576).toFixed(1),
|
|
348
366
|
heap : (p.mem.heapTotal / 1048576).toFixed(1),
|
|
349
367
|
heapused : (p.mem.heapUsed / 1048576).toFixed(1),
|
|
350
368
|
external : (p.mem.external / 1048576).toFixed(1),
|
|
351
369
|
},
|
|
352
370
|
conn : p.conn
|
|
353
|
-
})
|
|
371
|
+
})
|
|
354
372
|
}
|
|
355
|
-
|
|
373
|
+
|
|
374
|
+
return lj
|
|
356
375
|
}
|
|
357
376
|
|
|
358
|
-
if (type === '
|
|
359
|
-
let
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
},
|
|
367
|
-
https: this.config.https,
|
|
368
|
-
http2: this.config.http2,
|
|
369
|
-
workers : this.workers
|
|
370
|
-
};
|
|
371
|
-
|
|
372
|
-
return JSON.stringify(loadjson);
|
|
377
|
+
if (type === 'orgobj') {
|
|
378
|
+
let lj = this.loadjson
|
|
379
|
+
lj.CPULoadavg['1m'] = oavg[0].toFixed(2)
|
|
380
|
+
lj.CPULoadavg['5m'] = oavg[1].toFixed(2)
|
|
381
|
+
lj.CPULoadavg['15m'] = oavg[2].toFixed(2)
|
|
382
|
+
lj.workers = this.workers
|
|
383
|
+
|
|
384
|
+
return lj
|
|
373
385
|
}
|
|
374
386
|
|
|
375
|
-
return
|
|
387
|
+
return null
|
|
376
388
|
}
|
|
377
389
|
|
|
378
390
|
}
|
package/src/topbit.js
CHANGED
|
@@ -164,7 +164,7 @@ class Topbit {
|
|
|
164
164
|
* - maxFormLength 在multipart/form-data类型提交数据时,单个form项的最大值,默认为1000000字节。
|
|
165
165
|
* - errorHandle 收集错误并处理的函数,默认是输出错误信息,接收参数为两个,第一个是错误信息,第二个是错误的名字描述。
|
|
166
166
|
* - ignoreSlash 忽略末尾的/,默认为true。
|
|
167
|
-
* - maxLoadRate 在自动创建子进程平衡负载模式,最大子进程负载率限制:
|
|
167
|
+
* - maxLoadRate 在自动创建子进程平衡负载模式,最大子进程负载率限制:0.15 ~ 0.98。
|
|
168
168
|
* - streamTimeout http2Stream超时,若不设置,默认采用timeout的设置。
|
|
169
169
|
*/
|
|
170
170
|
constructor(options={}) {
|
|
@@ -272,7 +272,7 @@ class Topbit {
|
|
|
272
272
|
|
|
273
273
|
requestTimeout: 65_000,
|
|
274
274
|
|
|
275
|
-
maxLoadRate: 75
|
|
275
|
+
maxLoadRate: 0.75
|
|
276
276
|
};
|
|
277
277
|
|
|
278
278
|
this.whoami = 'topbit';
|
|
@@ -343,11 +343,11 @@ class Topbit {
|
|
|
343
343
|
break;
|
|
344
344
|
|
|
345
345
|
case 'monitorTimeSlice':
|
|
346
|
-
optionsCheck(k, options[k], this.config, {type: 'number', min: 5, max:
|
|
346
|
+
optionsCheck(k, options[k], this.config, {type: 'number', min: 5, max: 10000});
|
|
347
347
|
break;
|
|
348
|
-
|
|
348
|
+
|
|
349
349
|
case 'maxLoadRate':
|
|
350
|
-
optionsCheck(k, options[k], this.config, {type: 'number', min:
|
|
350
|
+
optionsCheck(k, options[k], this.config, {type: 'number', min: 0.15, max: 0.98});
|
|
351
351
|
break;
|
|
352
352
|
|
|
353
353
|
case 'maxFiles':
|
|
@@ -364,13 +364,13 @@ class Topbit {
|
|
|
364
364
|
case 'logHistory':
|
|
365
365
|
optionsCheck(k, options[k], this.config, {type: 'number', min: 1});
|
|
366
366
|
break;
|
|
367
|
-
|
|
367
|
+
|
|
368
368
|
case 'logType':
|
|
369
369
|
optionsCheck(k, options[k], this.config, {list: ['stdio','file', '']});
|
|
370
370
|
break;
|
|
371
371
|
|
|
372
372
|
case 'loadInfoType':
|
|
373
|
-
optionsCheck(k, options[k], this.config, {list: ['
|
|
373
|
+
optionsCheck(k, options[k], this.config, {list: [null, 'null', 'text', 'obj', 'orgobj']});
|
|
374
374
|
break;
|
|
375
375
|
|
|
376
376
|
case 'loadMonitor':
|
|
@@ -1110,7 +1110,7 @@ class Topbit {
|
|
|
1110
1110
|
total: 0
|
|
1111
1111
|
},
|
|
1112
1112
|
cpu: {user:0, system:0},
|
|
1113
|
-
cputm :
|
|
1113
|
+
cputm : 1000000
|
|
1114
1114
|
};
|
|
1115
1115
|
|
|
1116
1116
|
});
|