Skip to content

Commit dffe295

Browse files
committed
feat(process): auto load processMap while booting
1 parent 9aba0e1 commit dffe295

File tree

4 files changed

+139
-41
lines changed

4 files changed

+139
-41
lines changed

core.js

+38-36
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
const Koa = require('koa') // Koa v2
55
const winston = require('winston')
66
const nconf = require('nconf')
7-
const path = require('path')
7+
// const path = require('path')
88
const colors = require('colors/safe')
99
// const pkg = require('./package.json')
1010
// const mail = require('./src/mail')
@@ -27,50 +27,29 @@ if (program.dev) {
2727
// Register Server
2828
const app = new Koa()
2929

30-
// Load CronJob
31-
const childProcess = require('child_process')
32-
function spawnCronProcess () {
33-
const child = childProcess.fork(path.join(__dirname, './src/cron.js'), {
34-
env: {
35-
dev: program.dev
30+
// Load childProcesses
31+
let childProcessList = []
32+
async function registerProcesses () {
33+
const processesMap = require('./processes')
34+
const processesToStart = []
35+
const isDev = program.dev
36+
for (const process of processesMap) {
37+
if ((process.isDev && isDev) || (process.isProd && !isDev) || (!process.isDev && !process.isDev)) {
38+
processesToStart.push(process)
3639
}
37-
})
38-
child.on('message', (message) => {
39-
if (message === 'loaded') {
40-
winston.verbose('[init] all cronJobs are loaded.')
41-
} else if (message.key) {
42-
if (message.key === 'error') {
43-
console.log(colors.red(message.data))
44-
winston.error('[init] error was thrown while loading cron jobs, process existing.')
45-
}
46-
}
47-
})
48-
// register master exit process
49-
let masterExitFlag = false
50-
process.on('exit', () => {
51-
// kill child process
52-
masterExitFlag = true
53-
child.send({
54-
key: 'exit',
55-
data: ''
56-
})
57-
})
58-
child.on('exit', () => {
59-
if (!masterExitFlag) {
60-
winston.warn('[cronJob] cron job process is exited. Try to respawn it.')
61-
spawnCronProcess()
62-
}
63-
})
40+
}
41+
const { staticProcess } = require('./src/process')
42+
processesToStart.forEach(v => staticProcess().spawnProcess(v.path, v.name, v.messageListener))
43+
childProcessList = staticProcess().ProcessList
6444
}
65-
spawnCronProcess()
6645

6746
// Register Middlewares (Plugins)
6847
async function registerMiddlewares () {
6948
require('./src/middleware').register(app, program.dev)
7049
}
7150

7251
// Run Task
73-
// TODO: crate a task tree file
52+
// TODO: create a task tree file
7453
const { Task: updateSentencesTask } = require('./src/task/updateSentencesTask')
7554

7655
// Load Route
@@ -94,10 +73,33 @@ async function registerRoutes (routes) {
9473
}
9574
}
9675

76+
// handle the process exit event
77+
function handleProcessExitSignal (signal) {
78+
winston.verbose('[core] receive signal: ' + colors.yellow(signal) + ', starting the exit produre.')
79+
for (const child of childProcessList) {
80+
child.instance.kill('SIGTERM') // teng-koa exit signal code
81+
}
82+
winston.info('[core] Web server is shut down, Bye!')
83+
process.exit(0)
84+
}
85+
process.on('SIGINT', handleProcessExitSignal) // Ctrl + C
86+
process.on('SIGTERM', handleProcessExitSignal)
87+
88+
process.on('exit', (code) => { // handle unexpected exit event
89+
if (code) { // ignore zero exit code
90+
winston.error('[core] receiving exit code: ' + code + ', process will be destoryed.')
91+
for (const child of childProcessList) {
92+
child.instance.kill('SIGTERM') // teng-koa exit signal code
93+
}
94+
winston.info('[core] Web server is shut down, Bye!')
95+
}
96+
})
97+
9798
// Start Server
9899
async function start () {
99100
try {
100101
await updateSentencesTask()
102+
await registerProcesses()
101103
await registerMiddlewares()
102104
const Routes = require('./src/route')
103105
await registerRoutes(new Routes())

processes.js

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// This file is a map of child processes
2+
const path = require('path')
3+
const winston = require('winston')
4+
const colors = require('colors')
5+
6+
module.exports = [
7+
{
8+
path: path.join(__dirname, './src/cron.js'), // The absolute path of the process file
9+
name: 'cronJob', // The name of the process module
10+
messageListener: (message) => {
11+
if (message === 'loaded') {
12+
winston.verbose('[init] all cronJobs are loaded.')
13+
} else if (message.key) {
14+
if (message.key === 'error') {
15+
console.log(colors.red(message.data))
16+
winston.error('[init] error was thrown while loading cron jobs, process existing.')
17+
process.exit(1)
18+
}
19+
}
20+
}, // the handler of the receiving message
21+
isDev: false, // if set true, this process will start only in Dev
22+
isProd: false // if set true, this process will start only in prod
23+
// if isDev and isProd both be set false, process will both start in Dev and Prod
24+
}
25+
]

src/cron.js

+3-5
Original file line numberDiff line numberDiff line change
@@ -102,11 +102,9 @@ if (process.env && process.env.dev) {
102102
winston.level = 'verbose'
103103
nconf.set('dev', true)
104104
}
105-
106-
process.on('message', message => {
107-
if (message.key === 'exit') {
108-
winston.verbose('[cronJob] receive exit signal, cron process exiting.')
109-
process.exit()
105+
process.on('exit', (code) => {
106+
if (code && code === 1000) {
107+
winston.info('[cronJob] receiving exiting signal, cronJob process exits.')
110108
}
111109
})
112110
Cron.load()

src/process.js

+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// This module is intended to provide a helper of generating child processes
2+
const path = require('path')
3+
const childProcess = require('child_process')
4+
const winston = require('winston')
5+
const nconf = require('nconf')
6+
const colors = require('colors')
7+
8+
class Process {
9+
constructor () {
10+
/**
11+
* The childProcessList is a processes' instances collection
12+
* struct is:
13+
* [
14+
* {
15+
* name: String, // moduleName,
16+
* instance: Class/Object // the instance of the childProcess
17+
* },
18+
* ...
19+
* ]
20+
*/
21+
this.childProcessList = []
22+
}
23+
24+
get ProcessList () {
25+
return this.childProcessList
26+
}
27+
28+
spawnProcess (execFileAbsolutePath, moduleName, messageLisenner = null) {
29+
const child = childProcess.fork(path.join(execFileAbsolutePath), {
30+
env: {
31+
dev: !!nconf.get('dev')
32+
}
33+
})
34+
this.childProcessList.push({
35+
instance: child,
36+
name: moduleName
37+
})
38+
const ml = typeof messageLisenner === 'function' ? messageLisenner : (message) => {
39+
if (message && message.key === 'loaded') {
40+
winston.verbose('[' + moduleName + '] process are started.')
41+
}
42+
}
43+
child.on('message', ml)
44+
child.on('exit', this.handleChildProcessExitEvent(moduleName, execFileAbsolutePath, ml))
45+
}
46+
47+
handleChildProcessExitEvent (moduleName, path, messageLisenner) {
48+
return (code, signal) => {
49+
if (code === null && !signal) {
50+
winston.warn('[' + moduleName + '] process is exited accidentally. Try to respawn it.')
51+
this.spawnProcess(path, moduleName, messageLisenner)
52+
} else if (code > 0) { // errors might be thrown
53+
winston.error('[' + moduleName + '] child process exited with code: ' + code + ', master process exits to ensure the stablity.')
54+
process.exit(1)
55+
} else if (signal) { // exist ignore
56+
winston.info('[' + moduleName + '] process is exited due to receive sinal: ' + colors.blue(signal))
57+
} // ignore exit code: 0
58+
}
59+
}
60+
}
61+
62+
let processes = null
63+
function staticProcess () {
64+
if (!processes) {
65+
processes = new Process()
66+
}
67+
return processes
68+
}
69+
70+
module.exports = {
71+
Process,
72+
staticProcess
73+
}

0 commit comments

Comments
 (0)