集群管理
Node.js 是一个基于Chrome V8 引擎的JavaScript 运行环境。Node.js 使用了一个事件驱动、非阻塞式I/O 的模型,使其轻量又高效。Node.js 没有使用操作系统的线程模型,而是选择了更轻量级的事件循环机制。换句话说,Node.js 进程是单线程的,而且不可能也不能阻塞,如果阻塞了唯一的线程程序就不响应了。单个线程的 Node 通常只能在一个 CPU 核心上执行,如果服务器有更多的核心也用不到。想想是不是觉得浪费资源?
其实 Node 通过另外一种方式解决了这个问题,就是可以启动多个进程。比如说有 4 个 CPU 核心,可以选择启动 4 个工作进程。下面介绍的代码位于 app.js 中。
const config = require('./config')('server-config.json'),
log = require('./log')(config.env, console),
os = require('os'),
numCPUs = os.cpus().length,
fs = require('fs'),
cluster = require('cluster'),
worker = require('./worker'),
pidFilePath = 'hawkey.pid'
if(cluster.isMaster) {
// 主进程
// 输出主进程 ID 到文件中,方便通过脚本读取该文件获取进程号,进行启动停止进程的操作
fs.writeFileSync(pidFilePath, `${process.pid}`)
console.info(`master.process [${process.pid}] running`)
// fork 出 cpu 核心数量的 工作进程
for(let i = 0; i < numCPUs; i++) {
let wk = cluster.fork()
console.info(`master.process [${process.pid}], lanch worker-${i} [${wk.process.pid}]`)
}
cluster.on('exit', (wk, code, signal) => {
console.info(`worker.process [${wk.process.pid}] exit, code [${code}], signal [${signal}]`)
})
// 优雅关闭主进程,进而优雅的关闭所有工作进程
// 主进程会给所有工作进程发送 shutdown 信号,工作进程收到该信息号后,会主动 shutdown
// 等工作进程 shudown 后,主进程会 kill 掉工作进程
let shutdown = () => {
for(let id in cluster.workers) {
let wk = cluster.workers[id]
wk.send('shutdown', (err) => {
if(!err) {
if(!wk.isDead()) {
wk.kill()
}
} else {
console.error(`master send shutdown to worker [${wk.process.pid}] error`, err)
wk.process.kill()
}
})
}
// delete .pid file
fs.unlink(pidFilePath, (err) => {
if (!err) {
console.debug('delete .pid file')
} else {
console.error('delete .pid file error', err)
}
})
}
// 主进程注册信号处理程序
// 收到 Ctrl+C 或者 kill 信号后,主进程会主动 shutdown,并退出
process
.on('unhandledRejection', (reason, promise) => {
console.error(reason, 'Unhandled Rejection at Promise', promise, '@master')
})
.on('uncaughtException', (err, origin) => {
console.error('Uncaught Exception thrown', err.stack, origin, 'stop master')
shutdown()
process.exitCode = 1
})
.once('SIGINT', function (code) {
console.warn('SIGINT received...', code, 'stop master')
shutdown()
})
.once('SIGTERM', function (code) {
console.warn('SIGTERM received...', code, 'stop master')
shutdown()
})
} else {
// 工作进程,worker 组件代码位于 worker.js,前面已经介绍过了。
// worker 组件主要是启动服务器工作进程,并进入监听端口状态,完成具体的请求处理
worker(config)
console.info(`worker.process [${process.pid}] running`)
}
如上代码为主进程启动后,fork 出更多工作进程来完成实际的请求响应处理。代码上加了注释,比较好理解。