@nsshunt/stsappframework 2.19.205 → 2.19.206
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/masterprocessbase.js +18 -68
- package/dist/masterprocessbase.js.map +1 -1
- package/dist/processbase.js +59 -1
- package/dist/processbase.js.map +1 -1
- package/dist/processoptions.js +9 -0
- package/dist/processoptions.js.map +1 -1
- package/dist/serverprocessbase.js +342 -0
- package/dist/serverprocessbase.js.map +1 -0
- package/dist/singleprocessbase.js +13 -220
- package/dist/singleprocessbase.js.map +1 -1
- package/dist/tcpclient/app.js +21 -0
- package/dist/tcpclient/app.js.map +1 -0
- package/dist/tcpclient/app2.js +55 -0
- package/dist/tcpclient/app2.js.map +1 -0
- package/dist/tcpclientserver/app.js +15 -0
- package/dist/tcpclientserver/app.js.map +1 -0
- package/dist/tcpclientserver/appConfig.js +61 -0
- package/dist/tcpclientserver/appConfig.js.map +1 -0
- package/dist/workerprocessbase.js +15 -224
- package/dist/workerprocessbase.js.map +1 -1
- package/package.json +3 -2
- package/run.sh +24 -0
- package/runc1.sh +24 -0
- package/runc2.sh +24 -0
- package/src/commonTypes.ts +1 -1
- package/src/masterprocessbase.ts +11 -66
- package/src/processbase.ts +62 -2
- package/src/processoptions.ts +9 -9
- package/src/serverprocessbase.ts +390 -0
- package/src/singleprocessbase.ts +20 -254
- package/src/tcpclient/app.ts +19 -0
- package/src/tcpclient/app2.ts +55 -0
- package/src/tcpclientserver/app.ts +9 -0
- package/src/tcpclientserver/appConfig.ts +66 -0
- package/src/workerprocessbase.ts +24 -251
- package/types/commonTypes.d.ts +1 -1
- package/types/commonTypes.d.ts.map +1 -1
- package/types/masterprocessbase.d.ts.map +1 -1
- package/types/processbase.d.ts +4 -0
- package/types/processbase.d.ts.map +1 -1
- package/types/processoptions.d.ts +8 -8
- package/types/processoptions.d.ts.map +1 -1
- package/types/serverprocessbase.d.ts +21 -0
- package/types/serverprocessbase.d.ts.map +1 -0
- package/types/singleprocessbase.d.ts +3 -7
- package/types/singleprocessbase.d.ts.map +1 -1
- package/types/tcpclient/app.d.ts +2 -0
- package/types/tcpclient/app.d.ts.map +1 -0
- package/types/tcpclient/app2.d.ts +2 -0
- package/types/tcpclient/app2.d.ts.map +1 -0
- package/types/tcpclientserver/app.d.ts +2 -0
- package/types/tcpclientserver/app.d.ts.map +1 -0
- package/types/tcpclientserver/appConfig.d.ts +3 -0
- package/types/tcpclientserver/appConfig.d.ts.map +1 -0
- package/types/workerprocessbase.d.ts +2 -7
- package/types/workerprocessbase.d.ts.map +1 -1
package/runc2.sh
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# openssl req -nodes -new -x509 -keyout server.key -out server.cert
|
|
3
|
+
clear; \
|
|
4
|
+
export STS_PROJ_ROOT=./..; \
|
|
5
|
+
STSENVFILE=$STS_PROJ_ROOT/stsglobalresources/.env \
|
|
6
|
+
DB_SCRIPT_FOLDER=$STS_PROJ_ROOT/stsglobalresources/db-scripts \
|
|
7
|
+
REST01_PORT=3003 \
|
|
8
|
+
REST01_HOST_PORT=3003 \
|
|
9
|
+
REST01_SERVICE_NAME="STSRest01-3003" \
|
|
10
|
+
REST01_API_IDENTIFIER="https://stsmda.com.au/stsrest01api/v1.0/" \
|
|
11
|
+
REST01_ENDPOINT="https://stsrest.stsmda.org" \
|
|
12
|
+
MAX_CPU=1 \
|
|
13
|
+
AS_ENDPOINT=https://stscore.stsmda.org \
|
|
14
|
+
AS_HOST_PORT=3002 \
|
|
15
|
+
AS_PORT=3002 \
|
|
16
|
+
DB_HOST=localhost \
|
|
17
|
+
DB_PORT=5432 \
|
|
18
|
+
DB_PASSWORD=postgres \
|
|
19
|
+
HTTPS_SERVER_KEY_PATH=/etc/letsencrypt/live/stsmda.org/privkey.pem \
|
|
20
|
+
HTTPS_SERVER_CERT_PATH=/etc/letsencrypt/live/stsmda.org/fullchain.pem \
|
|
21
|
+
DEBUG=proc* \
|
|
22
|
+
PUBLISH_DEBUG=false \
|
|
23
|
+
UV_THREADPOOL_SIZE=64 \
|
|
24
|
+
node dist/tcpclient/app2
|
package/src/commonTypes.ts
CHANGED
|
@@ -70,7 +70,7 @@ export interface ISingleProcessBase extends IProcessBase {
|
|
|
70
70
|
get expressServer(): any // STSExpressServer - need to put interface in common types - circular reference
|
|
71
71
|
SetupServer(): Promise<boolean>
|
|
72
72
|
SetupServerEx: () => Promise<void>
|
|
73
|
-
|
|
73
|
+
TerminateApplication(): Promise<void>
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
export interface IWorkerProcessBase extends IProcessBase {
|
package/src/masterprocessbase.ts
CHANGED
|
@@ -7,7 +7,6 @@ import axios from 'axios';
|
|
|
7
7
|
import cluster, { Worker } from 'node:cluster'
|
|
8
8
|
|
|
9
9
|
import os from 'os';
|
|
10
|
-
let numCPUs = os.cpus().length;
|
|
11
10
|
|
|
12
11
|
import colors from 'colors'
|
|
13
12
|
|
|
@@ -26,7 +25,7 @@ import debugModule from 'debug'
|
|
|
26
25
|
import { Gauge, GaugeTypes, InstrumentGaugeTelemetry, InstrumentGaugeOptions, InstrumentHistogramTelemetry } from '@nsshunt/stsinstrumentation'
|
|
27
26
|
import { GetFirstNetworkInterface } from './network'
|
|
28
27
|
|
|
29
|
-
import { ProcessOptions } from './processoptions'
|
|
28
|
+
import { ProcessOptions, STSServerType } from './processoptions'
|
|
30
29
|
import { ProcessBase } from './processbase';
|
|
31
30
|
import { IMasterProcessBase } from './commonTypes';
|
|
32
31
|
|
|
@@ -120,15 +119,19 @@ export class MasterProcessBase extends ProcessBase implements IMasterProcessBase
|
|
|
120
119
|
this.#metricsServer = express();
|
|
121
120
|
this.#aggregatorRegistry = new AggregatorRegistry();
|
|
122
121
|
|
|
123
|
-
|
|
122
|
+
switch (this.options.serverType) {
|
|
123
|
+
case STSServerType.EXPRESS_TLS : {
|
|
124
124
|
const options = {
|
|
125
125
|
key: fs.readFileSync(this.options.httpsServerKeyPath),
|
|
126
126
|
cert: fs.readFileSync(this.options.httpsServerCertificatePath)
|
|
127
127
|
};
|
|
128
128
|
this.#httpServer = createServerHttps(options, this.#metricsServer);
|
|
129
|
-
}
|
|
129
|
+
}
|
|
130
|
+
break;
|
|
131
|
+
case STSServerType.EXPRESS : {
|
|
130
132
|
this.#httpServer = createServer(this.#metricsServer);
|
|
131
133
|
}
|
|
134
|
+
}
|
|
132
135
|
//this.#httpServer.maxConnections = 50;
|
|
133
136
|
|
|
134
137
|
this.#metricsServer.get('/cluster_metrics', async (req: any, res: any) => {
|
|
@@ -388,49 +391,9 @@ export class MasterProcessBase extends ProcessBase implements IMasterProcessBase
|
|
|
388
391
|
this.ProcessStartup();
|
|
389
392
|
const LogEx = this.LogEx;
|
|
390
393
|
|
|
391
|
-
/*
|
|
392
|
-
if (this.instruments !== null) {
|
|
393
|
-
this.instruments[Gauge.LOGGER].consoleLogging = this.options.consoleLogging;
|
|
394
|
-
this.instruments[Gauge.LOGGER].instrumentLogging = this.options.instrumentLogging;
|
|
395
|
-
}
|
|
396
|
-
*/
|
|
397
|
-
|
|
398
|
-
// https://systeminformation.io/
|
|
399
|
-
const valueObject = {
|
|
400
|
-
system: '*',
|
|
401
|
-
osInfo: '*',
|
|
402
|
-
cpu: '*',
|
|
403
|
-
mem: '*'
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
const sysinfo = await si.get(valueObject);
|
|
407
|
-
|
|
408
|
-
if (goptions.useCPUs > 0) {
|
|
409
|
-
if (goptions.useCPUs >= 1) {
|
|
410
|
-
numCPUs = goptions.useCPUs;
|
|
411
|
-
} else {
|
|
412
|
-
numCPUs = Math.round(sysinfo.cpu.cores * goptions.useCPUs);
|
|
413
|
-
}
|
|
414
|
-
} else {
|
|
415
|
-
numCPUs = sysinfo.cpu.physicalCores;
|
|
416
|
-
}
|
|
417
|
-
|
|
418
394
|
LogEx(`Service instance starting. Instance Id: [${this.options.serviceInstanceId}]`);
|
|
419
395
|
|
|
420
|
-
|
|
421
|
-
if (hostaddr !== null)
|
|
422
|
-
{
|
|
423
|
-
LogEx(`Host Address: ${hostaddr}`);
|
|
424
|
-
} else {
|
|
425
|
-
LogEx(`Unknown Host Address.`);
|
|
426
|
-
}
|
|
427
|
-
LogEx(`Server starting with ${numCPUs} Cores/Threads`);
|
|
428
|
-
|
|
429
|
-
LogEx(`Hostname: ${sysinfo.osInfo.hostname}`);
|
|
430
|
-
LogEx(`System: ${JSON.stringify(sysinfo.system)}`);
|
|
431
|
-
LogEx(`OS Info: ${JSON.stringify(sysinfo.osInfo)}`);
|
|
432
|
-
LogEx(`CPU: ${JSON.stringify(sysinfo.cpu)}`);
|
|
433
|
-
LogEx(`Memory: ${JSON.stringify(sysinfo.mem)}`);
|
|
396
|
+
this.LogSystemTelemetry();
|
|
434
397
|
|
|
435
398
|
// socket.io
|
|
436
399
|
// setup connections between the workers
|
|
@@ -447,18 +410,13 @@ export class MasterProcessBase extends ProcessBase implements IMasterProcessBase
|
|
|
447
410
|
this.#SetupPrometheusForMaster(LogEx);
|
|
448
411
|
}
|
|
449
412
|
|
|
413
|
+
const numCPUs = await this.GetNumCPUs();
|
|
450
414
|
for (let i=0; i < numCPUs; i++) {
|
|
451
415
|
this.#SpawnWorker();
|
|
452
416
|
}
|
|
453
417
|
|
|
454
418
|
this.#InitCluster(LogEx);
|
|
455
419
|
|
|
456
|
-
/*
|
|
457
|
-
if (this.publishBroker !== null) {
|
|
458
|
-
this.publishBroker.StartPublish();
|
|
459
|
-
}
|
|
460
|
-
*/
|
|
461
|
-
|
|
462
420
|
cluster.on('listening', (worker, address) =>
|
|
463
421
|
{
|
|
464
422
|
LogEx(`Worker process ${worker.process.pid} is listening at address: ${JSON.stringify(address)}`);
|
|
@@ -494,19 +452,6 @@ export class MasterProcessBase extends ProcessBase implements IMasterProcessBase
|
|
|
494
452
|
}
|
|
495
453
|
});
|
|
496
454
|
|
|
497
|
-
|
|
498
|
-
const GetSignalColour = (signal: any) =>
|
|
499
|
-
{
|
|
500
|
-
let msgcolor = null;
|
|
501
|
-
if (signal && signal === 'SIGINT')
|
|
502
|
-
{
|
|
503
|
-
msgcolor = colors.yellow;
|
|
504
|
-
} else {
|
|
505
|
-
msgcolor = colors.red;
|
|
506
|
-
}
|
|
507
|
-
return msgcolor;
|
|
508
|
-
};
|
|
509
|
-
|
|
510
455
|
// Terminate in order;
|
|
511
456
|
// forked worker threads (send signal)
|
|
512
457
|
// De-Register service
|
|
@@ -534,9 +479,9 @@ export class MasterProcessBase extends ProcessBase implements IMasterProcessBase
|
|
|
534
479
|
}
|
|
535
480
|
|
|
536
481
|
if (signal) {
|
|
537
|
-
LogEx(GetSignalColour(signal)(`Main Process (masterprocess): ${process.pid} received signal: ${signal}`));
|
|
482
|
+
LogEx(this.GetSignalColour(signal)(`Main Process (masterprocess): ${process.pid} received signal: ${signal}`));
|
|
538
483
|
} else {
|
|
539
|
-
LogEx(GetSignalColour(null)(`Main Process (masterprocess): ${process.pid} received Terminate without signal.`));
|
|
484
|
+
LogEx(this.GetSignalColour(null)(`Main Process (masterprocess): ${process.pid} received Terminate without signal.`));
|
|
540
485
|
}
|
|
541
486
|
|
|
542
487
|
LogEx(`De-Registering service.`);
|
package/src/processbase.ts
CHANGED
|
@@ -3,6 +3,8 @@ import { memoryUsage, exit } from 'process'
|
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import cluster from 'node:cluster'
|
|
5
5
|
import colors from 'colors'
|
|
6
|
+
import si from 'systeminformation' // https://systeminformation.io/
|
|
7
|
+
import { GetFirstNetworkInterface } from './network'
|
|
6
8
|
|
|
7
9
|
import debugModule from 'debug'
|
|
8
10
|
const debug = debugModule(`proc:${process.pid}:processBase`);
|
|
@@ -14,7 +16,7 @@ import { PGAccessLayer, PGPoolManager, PGPoolManagerEventName } from '@nsshunt/s
|
|
|
14
16
|
import { $Options } from '@nsshunt/stsconfig'
|
|
15
17
|
const goptions = $Options()
|
|
16
18
|
|
|
17
|
-
import { ProcessOptions } from './processoptions'
|
|
19
|
+
import { ProcessOptions, STSServerType } from './processoptions'
|
|
18
20
|
import { STSOptionsBase, JSONObject } from '@nsshunt/stsutils'
|
|
19
21
|
import { StatusCodes } from 'http-status-codes'
|
|
20
22
|
|
|
@@ -162,7 +164,7 @@ export abstract class ProcessBase extends STSOptionsBase implements IProcessBase
|
|
|
162
164
|
payloadType: InstrumentPayloadType.service,
|
|
163
165
|
consoleLogging: this.options.consoleLogging,
|
|
164
166
|
instrumentLogging: this.options.instrumentLogging,
|
|
165
|
-
httpServer: this.options.
|
|
167
|
+
httpServer: (this.options.serverType === STSServerType.EXPRESS),
|
|
166
168
|
instrumentationObservationInterval: this.options.instrumentationObservationInterval,
|
|
167
169
|
instrumentationTimeWindow: this.options.instrumentationTimeWindow,
|
|
168
170
|
instrumentDefinitions: this.GetAdditionalInstruments(),
|
|
@@ -325,4 +327,62 @@ export abstract class ProcessBase extends STSOptionsBase implements IProcessBase
|
|
|
325
327
|
}
|
|
326
328
|
return null;
|
|
327
329
|
}
|
|
330
|
+
|
|
331
|
+
GetNumCPUs = async (): Promise<number> => {
|
|
332
|
+
// https://systeminformation.io/
|
|
333
|
+
const valueObject = {
|
|
334
|
+
cpu: '*'
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
const sysinfo = await si.get(valueObject);
|
|
338
|
+
let numCPUs = 2;
|
|
339
|
+
if (goptions.useCPUs > 0) {
|
|
340
|
+
if (goptions.useCPUs >= 1) {
|
|
341
|
+
numCPUs = goptions.useCPUs;
|
|
342
|
+
} else {
|
|
343
|
+
numCPUs = Math.round(sysinfo.cpu.cores * goptions.useCPUs);
|
|
344
|
+
}
|
|
345
|
+
} else {
|
|
346
|
+
numCPUs = sysinfo.cpu.physicalCores;
|
|
347
|
+
}
|
|
348
|
+
return numCPUs;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
LogSystemTelemetry = async () => {
|
|
352
|
+
// https://systeminformation.io/
|
|
353
|
+
const valueObject = {
|
|
354
|
+
system: '*',
|
|
355
|
+
osInfo: '*',
|
|
356
|
+
cpu: '*',
|
|
357
|
+
mem: '*'
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
const sysinfo = await si.get(valueObject);
|
|
361
|
+
const numCPUs = await this.GetNumCPUs();
|
|
362
|
+
const hostname = sysinfo.osInfo.hostname;
|
|
363
|
+
|
|
364
|
+
const hostaddr = GetFirstNetworkInterface();
|
|
365
|
+
if (hostaddr !== null) {
|
|
366
|
+
this.LogEx(`Host Address: ${hostaddr}`);
|
|
367
|
+
} else {
|
|
368
|
+
this.LogEx(`Unknown Host Address.`);
|
|
369
|
+
}
|
|
370
|
+
this.LogEx(`Server starting with ${numCPUs} Cores/Threads`);
|
|
371
|
+
|
|
372
|
+
this.LogEx(`Hostname: ${hostname}`);
|
|
373
|
+
this.LogEx(`System: ${JSON.stringify(sysinfo.system)}`);
|
|
374
|
+
this.LogEx(`OS Info: ${JSON.stringify(sysinfo.osInfo)}`);
|
|
375
|
+
this.LogEx(`CPU: ${JSON.stringify(sysinfo.cpu)}`);
|
|
376
|
+
this.LogEx(`Memory: ${JSON.stringify(sysinfo.mem)}`);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
GetSignalColour = (signal: any) => {
|
|
380
|
+
let msgcolor = null;
|
|
381
|
+
if (signal === 'SIGINT') {
|
|
382
|
+
msgcolor = colors.yellow;
|
|
383
|
+
} else {
|
|
384
|
+
msgcolor = colors.red;
|
|
385
|
+
}
|
|
386
|
+
return msgcolor;
|
|
387
|
+
};
|
|
328
388
|
}
|
package/src/processoptions.ts
CHANGED
|
@@ -13,6 +13,14 @@ export interface STSExpressServer {
|
|
|
13
13
|
*/
|
|
14
14
|
export type ExpressRouteFactory = (app: express.Express, stsApp: IProcessBase) => void
|
|
15
15
|
|
|
16
|
+
export enum STSServerType {
|
|
17
|
+
NONE = "NONE",
|
|
18
|
+
EXPRESS = "EXPRESS",
|
|
19
|
+
EXPRESS_TLS = "EXPRESS_TLS",
|
|
20
|
+
TCPRAW_TLS = "TCPRAW_TLS",
|
|
21
|
+
JSONRPC2_TLS = "JSONRPC_TLS"
|
|
22
|
+
}
|
|
23
|
+
|
|
16
24
|
//export interface ProcessOptions extends JSONObject {
|
|
17
25
|
export interface ProcessOptions {
|
|
18
26
|
/**
|
|
@@ -27,15 +35,7 @@ export interface ProcessOptions {
|
|
|
27
35
|
|
|
28
36
|
// Service details
|
|
29
37
|
|
|
30
|
-
|
|
31
|
-
* Does this service run an HTTP server.
|
|
32
|
-
*/
|
|
33
|
-
httpServer: boolean
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Does this service run an HTTPS server. Note that httpServer must also be true if this value is true.
|
|
37
|
-
*/
|
|
38
|
-
httpsServer: boolean
|
|
38
|
+
serverType: STSServerType
|
|
39
39
|
|
|
40
40
|
/**
|
|
41
41
|
* HTTPS service public key path.
|
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
import debugModule from 'debug'
|
|
2
|
+
const debug = debugModule(`proc:${process.pid}`);
|
|
3
|
+
|
|
4
|
+
import { $Options } from '@nsshunt/stsconfig'
|
|
5
|
+
const goptions = $Options()
|
|
6
|
+
|
|
7
|
+
import fs from "fs"
|
|
8
|
+
import colors from 'colors'
|
|
9
|
+
|
|
10
|
+
import { createAdapter as clusterCreateAdapter } from '@socket.io/cluster-adapter'
|
|
11
|
+
import { createAdapter } from "@socket.io/redis-streams-adapter";
|
|
12
|
+
|
|
13
|
+
import { Gauge, InstrumentGaugeTelemetry } from '@nsshunt/stsinstrumentation'
|
|
14
|
+
import { JSONObject, Sleep } from '@nsshunt/stsutils'
|
|
15
|
+
|
|
16
|
+
import { ProcessOptions, STSServerType } from './processoptions'
|
|
17
|
+
import { ProcessBase } from './processbase';
|
|
18
|
+
|
|
19
|
+
import { register, Counter, collectDefaultMetrics, AggregatorRegistry } from 'prom-client'
|
|
20
|
+
|
|
21
|
+
import { createServer as createServerHttps } from 'https'
|
|
22
|
+
import { createServer } from 'http'
|
|
23
|
+
import tls from 'node:tls'
|
|
24
|
+
import net from 'node:net'
|
|
25
|
+
|
|
26
|
+
import { Server, ServerOptions } from "socket.io";
|
|
27
|
+
|
|
28
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
29
|
+
|
|
30
|
+
import { IPCMessage, IPCMessages, IPCMessagePayload, IPCMessageCommand, IWorkerProcessBase, ISocketIoHelper } from './commonTypes'
|
|
31
|
+
import { STSExpressServer } from './server';
|
|
32
|
+
import { Express } from 'express'
|
|
33
|
+
|
|
34
|
+
import { createClient, RedisClientType } from 'redis';
|
|
35
|
+
|
|
36
|
+
import jayson from 'jayson'
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* todo
|
|
40
|
+
* @typedef {Object} options - todo
|
|
41
|
+
* @property {boolean} [wssServer=false] - Create a web socket server on this worker instance
|
|
42
|
+
*/
|
|
43
|
+
export class ServerProcessBase extends ProcessBase
|
|
44
|
+
{
|
|
45
|
+
#masterProcessExitTime = goptions.masterProcessExitTime;
|
|
46
|
+
#io: Server | null = null;
|
|
47
|
+
#redisClient: RedisClientType | null = null;
|
|
48
|
+
#httpServer: any = null;
|
|
49
|
+
#expressServer: STSExpressServer | null = null;
|
|
50
|
+
#sockets: net.Socket[] = [ ];
|
|
51
|
+
#shuttingDown = false;
|
|
52
|
+
|
|
53
|
+
constructor(options: ProcessOptions) {
|
|
54
|
+
super(options);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
get httpServer() {
|
|
58
|
+
return this.#httpServer;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
get io() {
|
|
62
|
+
return this.#io;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
get expressServer(): STSExpressServer | null {
|
|
66
|
+
return this.#expressServer;
|
|
67
|
+
}
|
|
68
|
+
set expressServer(val: STSExpressServer | null) {
|
|
69
|
+
this.#expressServer = val;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Setup server to Prometheus scrapes:
|
|
73
|
+
#SetupPrometheusEndPoints = (expressServer: Express) => {
|
|
74
|
+
// AggregatorRegistry is required here in the worker as well as the master in order for prom-client to work correctly.
|
|
75
|
+
new AggregatorRegistry();
|
|
76
|
+
|
|
77
|
+
const prefix = 'sts_';
|
|
78
|
+
|
|
79
|
+
collectDefaultMetrics({
|
|
80
|
+
labels: { NODE_APP_INSTANCE: process.pid },
|
|
81
|
+
prefix: prefix
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
const c = new Counter({
|
|
85
|
+
name: 'sts_test_counter',
|
|
86
|
+
help: 'Example of a counter',
|
|
87
|
+
labelNames: ['code'],
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
setInterval(() => {
|
|
91
|
+
c.inc({ code: 200 });
|
|
92
|
+
}, 1000).unref();
|
|
93
|
+
|
|
94
|
+
setInterval(() => {
|
|
95
|
+
c.inc({ code: 400 });
|
|
96
|
+
c.inc({ code: 'worker_' + process.pid });
|
|
97
|
+
}, 500).unref();
|
|
98
|
+
|
|
99
|
+
expressServer.get('/metrics', async (req: any, res: any) => {
|
|
100
|
+
try {
|
|
101
|
+
res.set('Content-Type', register.contentType);
|
|
102
|
+
res.end(await register.metrics());
|
|
103
|
+
} catch (ex) {
|
|
104
|
+
res.status(500).end(ex);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
expressServer.get('/metrics/counter', async (req: any, res: any) => {
|
|
109
|
+
try {
|
|
110
|
+
res.set('Content-Type', register.contentType);
|
|
111
|
+
res.end(await register.getSingleMetricAsString('test_counter'));
|
|
112
|
+
} catch (ex) {
|
|
113
|
+
res.status(500).end(ex);
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
#SetupTLSServer = async (socket: net.Socket): Promise<void> => {
|
|
119
|
+
// Add a 'close' event handler to this instance of socket
|
|
120
|
+
console.log('CONNECTED: ' + socket.remoteAddress + ':' + socket.remotePort + ' ' + process.pid);
|
|
121
|
+
this.#sockets.push(socket);
|
|
122
|
+
|
|
123
|
+
const self = this;
|
|
124
|
+
socket.on('close', function(data: any) {
|
|
125
|
+
const index = self.#sockets.findIndex(function(o) {
|
|
126
|
+
return o.remoteAddress === socket.remoteAddress && o.remotePort === socket.remotePort;
|
|
127
|
+
})
|
|
128
|
+
if (index !== -1) self.#sockets.splice(index, 1);
|
|
129
|
+
console.log('CLOSED: ' + socket.remoteAddress + ' ' + socket.remotePort + ' ' + process.pid);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
socket.on('data', function(data: any) {
|
|
133
|
+
console.log('DATA ' + socket.remoteAddress + ': ' + socket.remotePort + ': ' + data);
|
|
134
|
+
socket.write(socket.remoteAddress + ':' + socket.remotePort + " said " + data + '\n');
|
|
135
|
+
|
|
136
|
+
// Write the data back to all the connected, the client will receive it as data from the server
|
|
137
|
+
/*
|
|
138
|
+
self.#sockets.forEach(function(socket, index, array) {
|
|
139
|
+
socket.write(socket.remoteAddress + ':' + socket.remotePort + " said " + data + '\n');
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
*/
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
#SetupRPCServer = async (socket: net.Socket): Promise<void> => {
|
|
147
|
+
console.log('CONNECTED: ' + socket.remoteAddress + ':' + socket.remotePort + ' ' + process.pid);
|
|
148
|
+
|
|
149
|
+
socket.on('close', function(data: any) {
|
|
150
|
+
console.log('CLOSED: ' + socket.remoteAddress + ' ' + socket.remotePort + ' ' + process.pid);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
socket.on('data', function(data: any) {
|
|
154
|
+
console.log('DATA ' + socket.remoteAddress + ': ' + data);
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
#SetupWSSServer = async (clusterMode: boolean): Promise<void> => {
|
|
159
|
+
// socket.io
|
|
160
|
+
// WebSocket
|
|
161
|
+
const options: Partial<ServerOptions> = {
|
|
162
|
+
transports: [ "websocket" ] // or [ "websocket", "polling" ] (to use long-poolling. Note that the order matters)
|
|
163
|
+
// The default path is /socket.io
|
|
164
|
+
// This can be changed with the path option as shown below
|
|
165
|
+
//,path: '/zzz'
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
//this.#io = require("socket.io")(this.#httpServer, options);
|
|
169
|
+
this.#io = new Server(this.#httpServer, options);
|
|
170
|
+
|
|
171
|
+
if (clusterMode) {
|
|
172
|
+
if (this.options.useRedisAdaptor) {
|
|
173
|
+
this.LogEx(`Using Redis for socket.io cluster management (worker)`);
|
|
174
|
+
if (this.options.redisAdaptorUrl) {
|
|
175
|
+
this.LogEx(`Redis url: [${this.options.redisAdaptorUrl}]`);
|
|
176
|
+
this.#redisClient = createClient({url: this.options.redisAdaptorUrl});
|
|
177
|
+
} else {
|
|
178
|
+
this.LogEx(`Redis url: [localhost]`);
|
|
179
|
+
this.#redisClient = createClient();
|
|
180
|
+
}
|
|
181
|
+
await this.#redisClient.connect();
|
|
182
|
+
this.#io.adapter(createAdapter(this.#redisClient) as any);
|
|
183
|
+
this.LogEx(`Redis successfully connected.`);
|
|
184
|
+
} else {
|
|
185
|
+
this.#io.adapter(clusterCreateAdapter() as any);
|
|
186
|
+
this.LogEx(`Using nodejs cluster mode for socket.io cluster management`);
|
|
187
|
+
}
|
|
188
|
+
} else {
|
|
189
|
+
this.LogEx(`Not using any adaptors for socket.io cluster management.}`);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// To use a seperate socket server, the code below can be applied.
|
|
193
|
+
// this.#io = require("socket.io")(options);
|
|
194
|
+
// this.#io.adapter(createAdapter());
|
|
195
|
+
// this.#io.listen(3006);
|
|
196
|
+
// LogEx(`socket.io init`);
|
|
197
|
+
|
|
198
|
+
this.#io.engine.on("connection_error", (err) => {
|
|
199
|
+
this.LogEx(err.req); // the request object
|
|
200
|
+
this.LogEx(err.code); // the error code, for example 1
|
|
201
|
+
this.LogEx(err.message); // the error message, for example "Session ID unknown"
|
|
202
|
+
this.LogEx(err.context); // some additional error context
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
#GetTLSOptions = (): JSONObject => {
|
|
207
|
+
return {
|
|
208
|
+
key: fs.readFileSync(this.options.httpsServerKeyPath),
|
|
209
|
+
cert: fs.readFileSync(this.options.httpsServerCertificatePath)
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
#SetupExpressServer = async (useTls: boolean): Promise<void> => {
|
|
214
|
+
if (useTls) {
|
|
215
|
+
this.#httpServer = createServerHttps(this.#GetTLSOptions(), (this.#expressServer as STSExpressServer).App);
|
|
216
|
+
} else {
|
|
217
|
+
this.#httpServer = createServer((this.#expressServer as STSExpressServer).App);
|
|
218
|
+
}
|
|
219
|
+
if (this.options.prometheusSupport === true) {
|
|
220
|
+
this.#SetupPrometheusEndPoints((this.#expressServer as STSExpressServer).App);
|
|
221
|
+
}
|
|
222
|
+
if (this.options.wssServer === true) {
|
|
223
|
+
await this.#SetupWSSServer(true);
|
|
224
|
+
}
|
|
225
|
+
// https://stackoverflow.com/questions/21342828/node-express-unix-domain-socket-permissions
|
|
226
|
+
//@@httpServer.listen('/tmp/stsrest01.sock').on('listening', () =>
|
|
227
|
+
//@@httpServer.listen('/var/run/sts/stsrest01.sock').on('listening', () =>
|
|
228
|
+
this.#httpServer.listen(this.options.listenPort, () => {
|
|
229
|
+
//@@chmodSync(this.options.port, 511);
|
|
230
|
+
}).on('listening', () =>
|
|
231
|
+
{
|
|
232
|
+
this.LogEx(`live on ${this.options.endpoint}:${this.options.listenPort}${this.options.apiRoot}`);
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
#SetupTCPRawServer = async (): Promise<void> => {
|
|
237
|
+
// The second parameter is the automatic listener for the secureConnection event from the tls.Server class
|
|
238
|
+
this.#httpServer = tls.createServer(this.#GetTLSOptions(), this.#SetupTLSServer);
|
|
239
|
+
this.#httpServer.listen(this.options.listenPort, 'stscore.stsmda.org', () => {
|
|
240
|
+
console.log('TCP Server is running on port ' + this.options.listenPort + '.');
|
|
241
|
+
}).on('listening', () =>
|
|
242
|
+
{
|
|
243
|
+
this.LogEx(`TCP live on ${this.options.endpoint}:${this.options.listenPort}${this.options.apiRoot}`);
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
#SetupJSONRPCServer = async (): Promise<void> => {
|
|
248
|
+
const jaysonServer = new jayson.server();
|
|
249
|
+
// Supported methods here - move somewhere else ...
|
|
250
|
+
jaysonServer.method("add", function(args: any, callback: any) {
|
|
251
|
+
callback(null, args[0] + args[1]);
|
|
252
|
+
});
|
|
253
|
+
this.#httpServer = jaysonServer.tls(this.#GetTLSOptions());
|
|
254
|
+
(this.#httpServer as tls.Server).on('secureConnection', this.#SetupRPCServer);
|
|
255
|
+
this.#httpServer.listen(this.options.listenPort, 'stscore.stsmda.org', () => {
|
|
256
|
+
console.log('JSON RPC 2.0 Server is running on port ' + this.options.listenPort + '.');
|
|
257
|
+
}).on('listening', () =>
|
|
258
|
+
{
|
|
259
|
+
this.LogEx(`JSON RPC 2.0 live on ${this.options.endpoint}:${this.options.listenPort}${this.options.apiRoot}`);
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
ProcessTerminating = async (): Promise<void> => {
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Terminate in order;
|
|
268
|
+
// forked worker threads (send signal)
|
|
269
|
+
// De-Register service
|
|
270
|
+
// systeminformation observers
|
|
271
|
+
// instrument timers (gauge etc.)
|
|
272
|
+
// publisher
|
|
273
|
+
// terminate UI (if loaded)
|
|
274
|
+
Terminate = async (clusterMode: boolean, clusterPerformExit: boolean, signal?: any): Promise<void> => {
|
|
275
|
+
if (this.#shuttingDown === false) {
|
|
276
|
+
this.#shuttingDown = true;
|
|
277
|
+
|
|
278
|
+
await this.ProcessTerminate();
|
|
279
|
+
|
|
280
|
+
await this.ProcessTerminating();
|
|
281
|
+
|
|
282
|
+
if (!clusterMode) {
|
|
283
|
+
if (this.GetUIController() !== null)
|
|
284
|
+
{
|
|
285
|
+
this.LogEx('Destroy the user interface controller.');
|
|
286
|
+
this.GetUIController().DestroyUI();
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if (signal) {
|
|
290
|
+
this.LogEx(this.GetSignalColour(signal)(`Main Process (singleprocess): ${process.pid} received signal: ${signal}`));
|
|
291
|
+
} else {
|
|
292
|
+
this.LogEx(this.GetSignalColour(null)(`Main Process (singleprocess): ${process.pid} received Terminate without signal.`));
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if (this.options.wssServer === true && this.#io !== null)
|
|
297
|
+
{
|
|
298
|
+
this.LogEx(`Disconnect Sockets.`);
|
|
299
|
+
if (this.socketIoHelper !== null) {
|
|
300
|
+
this.socketIoHelper.DisconnectSockets();
|
|
301
|
+
} else {
|
|
302
|
+
this.#io.disconnectSockets();
|
|
303
|
+
}
|
|
304
|
+
this.socketIoHelper = null;
|
|
305
|
+
this.#io = null;
|
|
306
|
+
// Note that this.#redisClient.disconnect() is not required becuase DisconnectSockets performs this action.
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
if (this.#httpServer) {
|
|
310
|
+
if (this.options.serverType === STSServerType.TCPRAW_TLS) {
|
|
311
|
+
this.#sockets.forEach((socket: net.Socket, index, array) => {
|
|
312
|
+
this.LogEx(`TCP Socket destroy, remote address: [${socket.remoteAddress}], remote port: [${socket.remotePort}]`.yellow);
|
|
313
|
+
socket.destroy();
|
|
314
|
+
//socket.end();
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
this.LogEx(`Closing httpServer.`);
|
|
318
|
+
await this.#httpServer.close();
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
if (this.options.useDatabase) {
|
|
322
|
+
this.LogEx(`Ending database connections and pools.`);
|
|
323
|
+
await this.TerminateDatabase();
|
|
324
|
+
//await this.accessLayer.enddatabase();
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
if (clusterMode) {
|
|
328
|
+
this.LogEx(`Performing exit value: [${clusterPerformExit}]`);
|
|
329
|
+
if (clusterPerformExit) {
|
|
330
|
+
this.LogEx(`Process will self terminate with process.exit(0).`);
|
|
331
|
+
} else {
|
|
332
|
+
this.LogEx(`Child process will not self terminate. Terminate will be handled by master process.`);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
if (this.InstrumentController && this.InstrumentController.Workers.length > 0) {
|
|
337
|
+
this.LogEx(`Ending publisher.`);
|
|
338
|
+
setTimeout(() => {
|
|
339
|
+
if (this.InstrumentController && this.InstrumentController.Workers.length > 0) {
|
|
340
|
+
this.InstrumentController.InstrumentTerminate();
|
|
341
|
+
}
|
|
342
|
+
}, 100);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
//@@ always return here appears to always cleanly exit
|
|
346
|
+
// and cleanly exit from socket.io cluster adaptor
|
|
347
|
+
// without return here, socket.io cluster adaptor terminates in an error state
|
|
348
|
+
// as the implementation relies on cluster.on to send messages to worker threads
|
|
349
|
+
// but these have already been closed from the process.exit(0) below.
|
|
350
|
+
|
|
351
|
+
await Sleep(1000); // Allow socket.io time to clean-up
|
|
352
|
+
|
|
353
|
+
if (clusterMode) {
|
|
354
|
+
if (clusterPerformExit) {
|
|
355
|
+
setTimeout(() => {
|
|
356
|
+
process.exit(0);
|
|
357
|
+
}, 0);
|
|
358
|
+
}
|
|
359
|
+
} else {
|
|
360
|
+
if (this.options.processExitOnTerminate && this.options.processExitOnTerminate === true) {
|
|
361
|
+
setTimeout(() => {
|
|
362
|
+
this.LogEx(`Performing process.exit(0).`);
|
|
363
|
+
process.exit(0);
|
|
364
|
+
}, this.#masterProcessExitTime); // Give the workers time to terminate gracefully
|
|
365
|
+
} else {
|
|
366
|
+
this.LogEx(`Performing process.exit(0) - Immediate.`);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
} else {
|
|
370
|
+
this.LogEx(`Process already terminating.`);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
SetupSTSServer = async(): Promise<void> => {
|
|
375
|
+
switch (this.options.serverType) {
|
|
376
|
+
case STSServerType.EXPRESS :
|
|
377
|
+
await this.#SetupExpressServer(false);
|
|
378
|
+
break;
|
|
379
|
+
case STSServerType.EXPRESS_TLS :
|
|
380
|
+
await this.#SetupExpressServer(true);
|
|
381
|
+
break;
|
|
382
|
+
case STSServerType.TCPRAW_TLS :
|
|
383
|
+
await this.#SetupTCPRawServer();
|
|
384
|
+
break;
|
|
385
|
+
case STSServerType.JSONRPC2_TLS :
|
|
386
|
+
await this.#SetupJSONRPCServer();
|
|
387
|
+
break;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|