topbit 3.0.8 → 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 CHANGED
@@ -915,7 +915,7 @@ app.run(1234)
915
915
  //展示负载信息,需要通过daemon接口开启cluster集群模式
916
916
  loadMonitor : true,
917
917
 
918
- //负载信息的类型,text 、json、--null
918
+ //负载信息的类型,text 、null, obj, orgobj
919
919
  //json类型是给程序通信使用的,方便接口开发
920
920
  loadInfoType : 'text',
921
921
 
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, json, --null
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/demo/allow.js CHANGED
@@ -1,8 +1,21 @@
1
1
  'use strict';
2
2
 
3
- const titbit = require('../src/topbit.js');
4
-
5
- const app = new titbit({
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
- showLoadInfo: true,
14
- loadInfoType : 'text',
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.to(c.name);
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.daemon(2034, 2);
116
+ app.autoWorker(3)
117
+
118
+ app.daemon(2034, 1)
@@ -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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "topbit",
3
- "version": "3.0.8",
3
+ "version": "3.1.0",
4
4
  "description": "A Server-side web framework support http/1.1 and http/2",
5
5
  "main": "src/topbit.js",
6
6
  "directories": {
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 = Date.now() - 1
31
+ this.cpuLastTime = hrtime.bigint() - 1n
28
32
 
29
- this.cpuNowTime = Date.now()
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 = parseInt(this.config.maxLoadRate * 10)
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 >= this.maxRate) {
69
- this.cpuHighRatio += 1
98
+ if (cpuratio > this.maxRate) {
99
+ this.cpuHighRatio++
70
100
  } else {
71
101
  if (this.cpuHighRatio > 0) {
72
- this.cpuHighRatio -= 1
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 -= 1
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 = Date.now()
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 += 1
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
- process.send({
186
- type : '_load',
187
- pid : process.pid,
188
- cpu : this.rundata.cpuTime,
189
- cputm : this.cpuNowTime - this.cpuLastTime,
190
- mem : this.rundata.mem,
191
- conn : this.rundata.conn
192
- }, err => {
193
- err && this.config.errorHandle(err, '--ERR-WORKER-SEND--')
194
- //if (err.code === 'ERR_IPC_CHANNEL_CLOSED') {}
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 == '--null') {
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 loadText = this.fmtLoadInfo( this.config.loadInfoType );
266
+ let load_info = this.fmtLoadInfo(this.config.loadInfoType);
232
267
 
233
268
  if (this.config.loadInfoFile === '--mem') {
234
- this.loadCache = loadText;
235
- }
236
- else if (this.loadfd !== null) {
237
-
238
- fs.write(this.loadfd, loadText, 0, (err, bytes, data) => {
239
- err && this.config.debug && this.config.errorHandle(err, '--ERR-WRITE-FILE--');
240
- if (!err) {
241
- fs.ftruncate(this.loadfd, bytes, e => {});
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(loadText);
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
- tmp.push((( t/(p.cputm * 10) ).toFixed(2) + '%').padEnd(8, ' '),
303
-
304
- (`${p.conn}`).padEnd(8, ' '),
305
-
306
- (p.mem.rss / 1048576).toFixed(1).padEnd(8, ' '),
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 === 'json') {
327
- let loadjson = {
328
- masterPid : process.pid,
329
- listen : `${this.rundata.host}:${this.rundata.port}`,
330
- CPULoadavg : {
331
- '1m' : oavg[0].toFixed(2),
332
- '5m' : oavg[1].toFixed(2),
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
- loadjson.workers.push({
343
- pid : p.pid,
344
- cpu : `${((p.cpu.user + p.cpu.system)/ (p.cputm * 10) ).toFixed(2)}%`,
345
- cputm : p.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
- return JSON.stringify(loadjson);
373
+
374
+ return lj
356
375
  }
357
376
 
358
- if (type === 'orgjson') {
359
- let loadjson = {
360
- masterPid : process.pid,
361
- listen : `${this.rundata.host}:${this.rundata.port}`,
362
- CPULoadavg : {
363
- '1m' : oavg[0].toFixed(2),
364
- '5m' : oavg[1].toFixed(2),
365
- '15m' : oavg[2].toFixed(2)
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 在自动创建子进程平衡负载模式,最大子进程负载率限制:25 ~ 98表示百分比。
167
+ * - maxLoadRate 在自动创建子进程平衡负载模式,最大子进程负载率限制:0.150.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: 5000});
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: 25, max: 98});
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: ['--null', 'text', 'json', 'orgjson']});
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 : 1000
1113
+ cputm : 1000000
1114
1114
  };
1115
1115
 
1116
1116
  });