下面代码不能命中 try catch
,原因是 nodejs 中每个事件循环都是一个单独的调用栈,执行到 setTimeout
里面代码时,老的调用栈已经回收,新的调用栈异常不会命中老调用栈中的函数
try {
interview(function() {
console.log('haha')
})
} catch (e) {
console.log('oh, no', e)
}
function interview(callback) {
setTimeout(() => {
if (Math.random() < 0.1) {
callback('success')
} else {
throw new Error('fail')
}
})
}
因此,nodejs 中更多的是使用 callback 或者 Promise 来返回调用结果。
如果使用 callback,则需要指定第一个参数为 Error,后续参数为正常调用后返回的结果。这是一个约定。
使用 callback 时需要慎重,因为使用不当会引发两个问题:回调地狱(callback 嵌套),回调并发(等待不少于两个带 callback 的函数执行完成后,再执行后续逻辑)
process.on('uncaughtException',function(err) {
// do some job
process.exit(1);
});
function exitWhenStopped() {
if (!stopping) {
stopping = true;
// do clear job
}
}
process.on('SIGINT', exitWhenStopped);
process.on('SIGTERM', exitWhenStopped);
process.on('SIGHUP', exitWhenStopped);
process.on('SIGUSR2', exitWhenStopped); // for nodemon restart
process.on('SIGBREAK', exitWhenStopped); // for windows ctrl-break
process.on('message', function(m) { // for PM2 under window with --shutdown-with-message
if (m === 'shutdown') { exitWhenStopped() }
});
cluster
时除了监听主进程异常外,还可以通过下面代码监听子进程状态
if (cluster.isMaster) {
// fork child process
cluster.on('exit', () => {
// some child process exit, do handle job
// example:
setTimeout(() => {
cluster.fork();
}, 5000);
})
}
node --prof
+ node --prof-process
node --inspect-brk
+ Chrome devtools在 nodejs 中可以使用 cluster
模块,实现类似 nginx 中 master-worker 的模型,提升 nodejs 服务器处理能力。使用 cluster
的好处是,不需要用户分心处理多进程协同,框架自己会进行进程间通信,协调各个进程。
需要注意的是,cluster
fork
出的子进程会完整拷贝整个 node 主进程,相当于多个一模一样的分身,这样会导致内存占用升高。进程间通信本身会额外消耗资源,且 node 自身也会使用到多进程。因为,一般经验是把 worker 的数量设置为 CPU 内核数量的一半(1/2),这样可以做到内存占用和处理能力的平衡。
一般设置进程数量为 CPU 内核数量的一半。
有内存泄漏时,进程的内存占用会越来越多,可以通过监测进程使用内存量,超过阈值时重启进程。
setInterval(() => {
if (process.memoryUsage().rss > 1000000000) {
// report and alert
process.exit(1)
}
}, 5000);
function listenChildProcessActiveState(worker) {
// clean zombie process
let missedPing = 0;
let inter = setInterval(() => {
worker.send('ping')
missedPing++;
if (missedPing >= 3) {
clearInterval(inter);
process.kill(worker.process.pid);
}
}, 3000)
worker.on('message', (msg) => {
if (msg === 'pong') {
missedPing--;
}
})
}
if (cluster.isMaster) {
for(let i = 0; i < limit; i++) {
// fork child process
let worker = cluster.fork();
listenChildProcessActiveState(worker);
}
// 子进程退出监测
cluster.on('exit', () => {
// some child process exit, do handle job
// example:
setTimeout(() => {
let worker = cluster.fork();
listenChildProcessActiveState(worker);
}, 5000);
})
} else {
process.on('message', (msg) => {
if (msg === 'ping') {
process.send('pong');
}
})
}