捕获并处理错误
您可以为此使用节点的内置domain module。
域提供了一种方法来处理多个不同的 IO 操作作为
单组。如果任何事件发射器或回调注册到
域发出错误事件,或抛出错误,然后域
对象将被通知,而不是丢失错误的上下文
在 process.on('uncaughtException') 处理程序中,或导致程序
立即退出并显示错误代码。
需要注意的非常重要的一点是:
域错误处理程序不能替代发生错误时关闭您的进程。
由于 throw 在 JavaScript 中的工作原理,几乎有
从来没有任何方法可以安全地“从你离开的地方继续”,而不会泄漏
引用,或创建其他某种未定义的脆弱状态。
由于您只是询问如何响应500 错误,因此我不会像节点文档那样讨论如何处理重新启动服务器等; 我强烈推荐taking a look at the example in the node docs。
他们的示例展示了如何捕获错误,将错误响应发送回客户端(如果可能),然后重新启动服务器。我将只显示域创建并发送回500 错误响应。 (请参阅下一节有关重新启动该过程的部分)
域的工作方式类似于将try/catch 放入createServer 回调中。在您的回调中:
- 创建一个新的域对象
- 收听域的
error 事件
- 将
req 和res 添加到域(因为它们是在域存在之前创建的)
-
run 域并调用您的请求处理程序(这就像 try/catch 的 try 部分)
类似这样的:
var domain = require('domain');
function handleRequest(req, res) {
// Just something to trigger an async error
setTimeout(function() {
throw Error("Some random async error");
res.end("Hello world!");
}, 100);
}
var server = require("http").createServer(function (req, res) {
var d = domain.create();
d.on('error', function(err) {
// We're in an unstable state, so shutdown the server.
// This will only stop new connections, not close existing ones.
server.close();
// Send our 500 error
res.statusCode = 500;
res.setHeader("content-type", "text/plain");
res.end("Server error: " + err.message);
});
// Since the domain was created after req and res, they
// need to be explictly added.
d.add(req);
d.add(res);
// This is similar to a typical try/catch, but the "catch"
// is now d's error event.
d.run(function() {
handleRequest(req, res);
});
}).listen(8080);
出错后重启进程
通过使用cluster 模块,您可以在出错后很好地重新启动进程。我基本上是从此处的节点文档中复制示例,但总体思路是从主进程启动多个工作进程。工作人员是处理传入连接的进程。如果其中一个有不可恢复的错误(即我们在上一节中捕获的错误),那么它将与主进程断开连接,发送 500 响应并退出。当主进程看到工作进程断开连接时,它就会知道发生了错误并启动了一个新的工作进程。由于有多个工作进程同时运行,因此如果其中一个出现故障,应该不会出现丢失传入连接的问题。
示例代码,复制自here:
var cluster = require('cluster');
var PORT = +process.env.PORT || 1337;
if (cluster.isMaster) {
// In real life, you'd probably use more than just 2 workers,
// and perhaps not put the master and worker in the same file.
//
// You can also of course get a bit fancier about logging, and
// implement whatever custom logic you need to prevent DoS
// attacks and other bad behavior.
//
// See the options in the cluster documentation.
//
// The important thing is that the master does very little,
// increasing our resilience to unexpected errors.
cluster.fork();
cluster.fork();
cluster.on('disconnect', function(worker) {
console.error('disconnect!');
cluster.fork();
});
} else {
// the worker
//
// This is where we put our bugs!
var domain = require('domain');
// See the cluster documentation for more details about using
// worker processes to serve requests. How it works, caveats, etc.
var server = require('http').createServer(function(req, res) {
var d = domain.create();
d.on('error', function(er) {
console.error('error', er.stack);
// Note: we're in dangerous territory!
// By definition, something unexpected occurred,
// which we probably didn't want.
// Anything can happen now! Be very careful!
try {
// make sure we close down within 30 seconds
var killtimer = setTimeout(function() {
process.exit(1);
}, 30000);
// But don't keep the process open just for that!
killtimer.unref();
// stop taking new requests.
server.close();
// Let the master know we're dead. This will trigger a
// 'disconnect' in the cluster master, and then it will fork
// a new worker.
cluster.worker.disconnect();
// try to send an error to the request that triggered the problem
res.statusCode = 500;
res.setHeader('content-type', 'text/plain');
res.end('Oops, there was a problem!\n');
} catch (er2) {
// oh well, not much we can do at this point.
console.error('Error sending 500!', er2.stack);
}
});
// Because req and res were created before this domain existed,
// we need to explicitly add them.
// See the explanation of implicit vs explicit binding below.
d.add(req);
d.add(res);
// Now run the handler function in the domain.
d.run(function() {
handleRequest(req, res);
});
});
server.listen(PORT);
}
// This part isn't important. Just an example routing thing.
// You'd put your fancy application logic here.
function handleRequest(req, res) {
switch(req.url) {
case '/error':
// We do some async stuff, and then...
setTimeout(function() {
// Whoops!
flerb.bark();
});
break;
default:
res.end('ok');
}
}
注意:我还是想强调一下,你应该看看domain module documentation,看看那里的例子和解释。它解释了其中大部分(如果不是全部)、其背后的原因以及您可能遇到的其他一些情况。