处理http服务器崩溃

我有一个非常基本的http服务器:

require("http").createServer(function (req, res) { res.end("Hello world!"); }).listen(8080); 

我怎样才能听服务器崩溃,所以我可以发送500状态码作为回应?

process.on("uncaughtException", handler)process级别工作,但我没有请求和响应对象。

我看到的一个可能的解决scheme是在createServercallback中使用try - catch语句,但是我正在考虑是否有更好的解决scheme。

我试图侦听server对象上的error事件,但没有任何反应:

 var s = require("http").createServer(function (req, res) { undefined.foo; // test crash res.end("Hello world!"); }); s.on("error", function () { console.log(arguments); }); s.listen(8080); 

捕捉和处理错误

您可以使用节点的内置域模块 。

域提供了一种将多个不同的IO操作作为一个组来处理的方法。 如果任何注册到域的事件发射器或callback发出错误事件或抛出错误,则会通知域对象,而不是丢失process.on('uncaughtException')处理器中错误的上下文,或导致程序立即退出并显示错误代码。

一个非常重要的事情要注意的是:

发生错误时,域error handling程序不能替代closures进程。

由于JavaScript在JavaScript中的工作原理,几乎没有任何方法可以安全地“捡起你离开的地方”,而不会泄漏引用或创build其他某种不明确的状态。

既然你只是问如何回应一个500错误,我不打算进入如何处理重启服务器等,就像节点文档一样; 我强烈build议查看节点文档中的示例 他们的示例显示如何捕获错误,将错误响应发送回客户端(如果可能),然后重新启动服务器。 我只会显示域创build并发回500错误响应。 (请参阅下一节关于重新启动进程)

域名的工作方式与在您的createServercallback中添加try / catch类似。 在你的callback中:

  1. 创build一个新的域对象
  2. 聆听域的error事件
  3. reqres添加到域中(因为它们是在域存在之前创build的)
  4. run域并调用你的请求处理程序(这就像try / catchtry部分)

像这样的东西:

 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响应,然后退出。 当主进程看到工作进程断开时,它会知道发生了错误,并启动一个新的工作。 由于一次运行多个工作进程,因此如果其中一个进程连接断开,则不应该存在丢失传入连接的问题。

示例代码,从这里复制:

 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模块的文档 ,看看那里的例子和解释。 它解释了大部分(如果不是全部的话),其背后的推理以及其他可能遇到的情况。