2

我正在使用 express 处理节点中的域和集群,并遇到了这种情况。我有一个集群,它为每个核心生成一个工作人员,我为每个工作人员创建一个快速服务器,该服务器使用每个请求域的策略来处理错误。

下面给出的解决方案按原样工作正常,但是当显式将请求和响应对象添加到域时,错误中间件将停止调用。我不知道为什么会引入这种行为。

问题:

  1. 为什么会发生?
  2. 我是否以正确的方式使用域?
  3. 我可以通过“process.domain”访问工作人员中的当前域还是需要做一些不同的事情?

提前致谢!

我的 app.js:

var express = require('express')
  , http = require('http')
  , path = require('path')
  , domain = require('domain')
  , cluster = require('cluster')
  , http = require('http')
  , numCPUs = require('os').cpus().length;

if (cluster.isMaster) {

  // fork workers
  for (var i = 0; i < numCPUs; i++) {
    cluster.fork();
  }

  // when a worker dies create a new one
  cluster.on('exit', function(worker, code, signal) {
    cluster.fork();
  });

} else {

  var app = express();

  //domains
  app.use(function domainMiddleware(req, res, next) {

    var reqDomain = domain.create();

    res.on('close', function () {
      reqDomain.dispose();
    });

    res.on('finish', function () {
      reqDomain.dispose();
    });

    reqDomain.on('error', function (err) {
      reqDomain.dispose();
      // delegate to express error-middleware
      next(err);
    });

    // Adding the request and response objects to the domain
    // makes the express error-middleware to not being called.
    // reqDomain.add(req);
    // reqDomain.add(res);

    reqDomain.run(next);
  });

  // all environments
  app.set('port', process.env.PORT || 3000);
  app.set('views', __dirname + '/views');
  app.set('view engine', 'jade');
  app.use(express.favicon());
  //app.use(express.logger('dev'));
  app.use(express.bodyParser());
  app.use(express.methodOverride());
  app.use(app.router);
  app.use(express.static(path.join(__dirname, 'public')));

  // for testing which cluster that serves the request
  app.get('/', function(req, res, next) {
    res.json(200, { id: cluster.worker.id });
  });
  app.get('/error', function(req, res, next) {
    var fs = require('fs');
    // intentionally force an error
    fs.readFile('', process.domain.intercept(function(data) { 
      // when using intercept we dont need this line anymore
      //if (err) throw err;
      res.send(data);
    }));
  });

  app.use(function(err, req, res, next) {
    console.log('ERROR MIDDLEWARE', err);
    res.writeHeader(500, {'Content-Type' : "text/html"});
    res.write("<h1>" + err.name + "</h1>");
    res.end("<p>" + err.message + "</p>");
  });

  http.createServer(app).listen(app.get('port'), function(){
    console.log('Express server listening on port ' + app.get('port'));
  });
}
4

1 回答 1

2

这是由于调用reqDomain.dispose(). 从dispose文档中:

dispose 方法销毁一个域,并尽最大努力清理与该域关联的所有 IO。流被中止、结束、关闭和/或销毁。计时器被清除。不再调用显式绑定的回调。因此引发的任何错误事件都将被忽略。

一旦reqres被添加到域中,处置域就会结束/关闭/销毁它们。您可以通过在回调res内部显式发送一些输出来测试这一点,但在处理域之前。reqDomain.on('error')

只需将调用next向上移动一行似乎就可以解决问题:

reqDomain.on('error', function (err) {
  next(err);
  reqDomain.dispose();
});

至于process.domain,我以前从未见过这个,并且对它的工作感到非常惊讶。我查看了源代码,发现以下内容

Domain.prototype.enter = function() {
  if (this._disposed) return;

  // note that this might be a no-op, but we still need
  // to push it onto the stack so that we can pop it later.
  exports.active = process.domain = this;
  stack.push(this);
};

因此,它似乎process.domain始终是“最新”域。就个人而言,我可能会将域附加到req对象或其他东西上,这样您就可以更明确地知道哪个域应该处理错误(尽管实际上它可能process.domain始终是您要查找的域)。

于 2013-05-27T00:15:02.130 回答