The following is a simple task to create a sub-Based fork child process nodejs while using prometheus exposed some simple metrics
using the express framework
Preparing the Environment
- Project structure
├── Dockerfile
├── README.md
├── app.js
├── docker-compose.yaml
├── grafana
│ └── metrics.json
├── metrics.js
├── package.json
├── Prometheus. YML
├── send_mail.js
├── taskstatus.js
├── utils.js
└── yarn.lock
- Structure Description
send_mail.js the code simple fork child process
taskstatus.js storage sub-process task status
utils.js is a simple tool modules, mainly for the clean-up has been completed subtasks
metrics.js to prometheus metrics defined
app core code .js express rest interface operations and processing tasks
docker-compose.yaml docker-compose family bucket running
Dockerfile nodejs application container of
grafana / metrics.json application grafana configuration for dashboard prometheus - Code Description
app.js core processing
const { fork } = require('child_process')
const express = require("express")
const util = require("./utils")
const uuid = require("uuid/v4")
const { child_process_status_all, child_process_status_pending, child_process_status_ok } = require("./taskstatus")
const { child_process_status_all_counter, child_process_status_pending_gauge, child_process_status_ok_counter, initMetrics, initGcStats, process_ok_clean_timer__status, up } = require("./metrics")
const app = express();
const main_process_id = process.pid;
let interval = false;
/**
* Metrics route register route registered prometheus metrcis
*/
app.get('/metrics', (req, res) => {
initMetrics(req, res);
})
/**
* Disable process clean timer disable a scheduled task to clean up
*/
app.get("/disable_timer", (req, res) => {
if (interval) {
range = false;
}
process_ok_clean_timer__status.set(0)
Res. send ({ timer_statuss: false})
})
/**
* Enable process clean timer enable a scheduled task to clean up the child process
*/
app.get("/enable_timer", (req, res) => {
if (interval == false) {
interval = true;
}
process_ok_clean_timer__status.set(1)
res.send({ timer_statuss: true })
})
/**
* for create process workers
*/
app.get('/endpoint', (req, res) => {
// fork another process child process to create and communicate the message (and some state prometheus metrics maintenance)
const myprocess = fork('./send_mail.js');
child_process_status_pending[myprocess.pid] = {
status: "pending"
}
child_process_status_all[myprocess.pid] = {
status: "pending"
}
child_process_status_all_counter.inc(1)
child_process_status_pending_gauge.inc(1)
console.log(`fork process pid:${myprocess.pid}`)
const mails = uuid();
// send list of e-mails to forked process
myprocess.send({ mails });
// listen for messages from forked process
myprocess.on('message', (message) => {
console.log(`Number of mails sent ${message.counter}`);
child_process_status_ok[myprocess.pid] = {
status: "ok"
}
child_process_status_ok_counter.inc(1)
child_process_status_pending_gauge.dec(1)
child_process_status_all[myprocess.pid] = {
status: "ok"
}
delete child_process_status_pending[myprocess.pid]
});
return res.json({ status: true, sent: true });
});
/**
* Child process call api for stop processed workers delete tasks completed
*/
app.get("/stop", (req, res) => {
util.stopProcess(main_process_id, (err, data) => {
if (err == null) {
res.send({ timer_clean_status: "ok" })
}
})
})
// init gc metrics gc metrcis exposure
initGcStats ()
// clean ok process timer task to clean up the timing of the completion of the process
setInterval(function () {
if (interval) {
util.stopProcess(main_process_id, (err, data) => {
if (err == null) {
console.log({ timer_clean_status: "ok" })
} else {
process_ok_clean_timer__status.set(0)
}
})
}
}, 10000)
// set metric status to up
up.set(1)
app.listen(8080, "0.0.0.0", () => {
console.log(`go to http://localhost:8080/ to generate traffic`)
}).on("error", () => {
up.set(0)
})
utils.js regular tasks cleanup module
const psTree = require("ps-tree")
const {spawn } = require('child_process')
const {child_process_status_ok} = require("./taskstatus")
const {process_ok_clean_timer__status} = require("./metrics")
/**
*
* @param {mainProcessID} mainProcessID
* @param {cb} callback for check status
*/
function stopProcess(mainProcessID,cb){
psTree(mainProcessID, function (err, children) {
if (err){
process_ok_clean_timer__status.set(0)
}
let pids = [];
for (const key in child_process_status_ok) {
if (child_process_status_ok.hasOwnProperty(key)) {
pids.push(key)
delete child_process_status_ok[key]
}
}
let info = children.filter(item => item.COMM == "ps" || item.COMMAND == "ps").map(function (p) { return p.PID })
pids.push(
console.log(`stop child process ids: ${JSON.stringify(pids)}`)
spawn('kill', ['-9'].concat(pids));
cb(null,"ok")
})
}
module.exports = {
stopProcess
};
metrics.js prometheus metrics module
is mainly defined metrics, and an initialization method
const Prometheus = require("prom-client")
const gcStats = require('prometheus-gc-stats')
module.exports = {
child_process_status_all_counter:new Prometheus.Counter({
name: 'child_process_status_all_total',
help: 'all running process',
labelNames: ['process_all']
}),
child_process_status_pending_gauge:new Prometheus.Gauge({
name: 'current_child_process_status_pending',
help: 'current pending process',
labelNames: ['process_pending']
}),
child_process_status_ok_counter:new Prometheus.Counter({
name: 'child_process_status_ok_total',
help: 'all ok process',
labelNames: ['process_ok']
}),
process_ok_clean_timer__status:new Prometheus.Gauge({
name: 'process_ok_clean_timer_status',
help: 'process_ok_clean_timer_status',
labelNames: ['process_timer']
}),
up:new Prometheus.Gauge({
name: 'up',
help: 'metrics_status',
labelNames: ['metrics_status']
}),
initGcStats: function(){
const startGcStats = gcStats(Prometheus.register)
startGcStats ()
},
initMetrics:function(req,res){
res.set('Content-Type', Prometheus.register.contentType)
res.end(Prometheus.register.metrics())
}
}
taskstatus.js state storage (It is simply a json object)
module.exports = {
child_process_status_all:{},
child_process_status_pending:{},
child_process_status_ok:{}
}
send_mail.js child process tasking
async function sendMultipleMails(mails) {
let sendMails = 0;
// logic for
// sending multiple mails
return sendMails;
}
// receive message from master process
process.on('message', async (message) => {
console.log("get messageId",message)
const numberOfMailsSend = await sendMultipleMails(message.mailsid);
setTimeout(()=>{
process.send({ counter: numberOfMailsSend });
},Number.parseInt(Math.random()*10000))
// send response to master process
});
For ease of use docker run Dockerfile
FROM node:12.14.0-alpine3.9
WORKDIR /app
COPY app.js /app/app.js
COPY package.json /app/package.json
COPY yarn.lock /app/yarn.lock
COPY metrics.js /app/metrics.js
COPY utils.js /app/utils.js
COPY taskstatus.js /app/taskstatus.js
COPY send_mail.js /app/send_mail.js
#CMD [ "yarn","app"]
EXPOSE 8080
RUN yarn
ENTRYPOINT [ "yarn","app" ]
docker-compose file mainly contains prometheus and grafana also nodejs application
version: "3"
services:
app:
build: ./
ports:
- "8080:8080"
image: dalongrong/node-process
grafana:
image: grafana / grafana
ports:
- "3000:3000"
prometheus:
image: prom/prometheus
volumes:
- "./prometheus.yml:/etc/prometheus/prometheus.yml"
ports:
- "9090:9090"
Start && effect
- start up
docker-compose up -d
- Simple pressure measurement
ab -n 200 -c 20 http://localhost:8080/endpoint
- grafana effect
Explanation
These are created based on a simple subtasks fork and nodejs of prometheus
Reference material
https://github.com/rongfengliang/node-process-fork-learning
https://nodejs.org/api/child_process.html#child_process_child_process_fork_modulepath_args_options