处理http服务器崩溃

Ion*_*zău 5 javascript http node.js

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

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

如何监听服务器崩溃,以便我可以发送500状态代码作为响应?

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

我看到的一个可能的解决方案是try - catchcreateServer回调中使用语句,但我在寻找是否有更好的解决方案.

我试着errorserver对象上侦听事件,但没有任何反应:

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);                                 
Run Code Online (Sandbox Code Playgroud)

Mik*_*e S 9

捕获和处理错误

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

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

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

域错误处理程序不能代替在发生错误时关闭进程.

根据throw如何在JavaScript中工作的本质,几乎从来没有任何方法可以安全地"拾取你离开的地方",而不会泄漏引用,或者创建一些其他类型的未定义的脆弱状态.

既然你只询问如何回应500错误,我就不会像节点文档那样处理如何处理重启服务器等问题.我强烈建议您查看节点文档中的示例.他们的示例演示了如何捕获错误,将错误响应发送回客户端(如果可能),然后重新启动服务器.我将只显示域名创建并发回500错误响应. (请参阅有关重新启动过程的下一部分)

域的工作方式类似于在回调中放置try/ .在你的回调中:catchcreateServer

  1. 创建一个新的域对象
  2. 聆听域名的error活动
  3. 添加reqres域(因为他们的存在域之前创建)
  4. run域并调用您的请求处理程序(这就像/ 的try一部分)trycatch

像这样的东西:

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); 
Run Code Online (Sandbox Code Playgroud)

发生错误后重新启动进程

通过使用该cluster模块,您可以在发生错误后很好地重新启动该过程.我基本上是从这里的节点文档复制示例,但一般的想法是从主进程启动多个工作进程.worker是处理传入连接的进程.如果其中一个具有不可恢复的错误(即我们在上一节中捕获的错误),那么它将与主进程断开连接,发送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');
  }
}
Run Code Online (Sandbox Code Playgroud)

注意:我仍然要强调您应该查看domain模块文档并查看其中的示例和说明.它解释了大部分(如果不是全部)这个,它背后的原因,以及你可能遇到的其他一些情况.