Node.js 将req和res显式添加到域don';t传播错误以表示中间件

Node.js 将req和res显式添加到域don';t传播错误以表示中间件,node.js,express,Node.js,Express,我正在用express在node中玩域和集群,并陷入了这种情况。我有一个集群,每个核心生成一个worker,我为每个worker创建一个express服务器,该服务器使用每个请求的域策略来处理错误 下面给出的解决方案可以正常工作,但是当向域显式添加请求和响应对象时,错误中间件将停止调用。我不知道为什么会出现这种行为 问题: 为什么会这样 我是否以正确的方式使用域 我可以通过“process.domain”访问工作进程中的当前域吗 还是我需要做些不同的事情 提前谢谢 我的app.js: var e

我正在用express在node中玩域和集群,并陷入了这种情况。我有一个集群,每个核心生成一个worker,我为每个worker创建一个express服务器,该服务器使用每个请求的域策略来处理错误

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

问题:

  • 为什么会这样
  • 我是否以正确的方式使用域
  • 我可以通过“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'));
      });
    }
    
    var express=require('express'))
    ,http=require('http')
    ,path=require('path'))
    ,domain=require('domain')
    ,cluster=require('cluster'))
    ,http=require('http')
    ,numpus=require('os').cpus().length;
    if(cluster.isMaster){
    //叉工
    对于(变量i=0;i”);
    });
    http.createServer(app.listen)(app.get('port'),function(){
    console.log('Express server监听端口'+app.get('port'));
    });
    }
    
    这是由于调用了
    reqDomain.dispose()
    。从:

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

    req
    res
    添加到域后,处理域。您可以通过在('error')回调上,但在处理域之前,显式地将一些输出发送到
    res
    中的
    reqDomain.on('error')
    回调来测试这一点

    只需将调用移动到
    next
    一行就可以解决问题:

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

    至于
    process.domain
    ,我以前从未见过这种方法,并且对它的有效性感到非常惊讶。我查看了源代码并:


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

    谢谢您的回答!通过将调用“next(err)”向上移动一行,您的正确操作将解决此问题,尽管可以在下一个中间件中触发某些异步操作之前释放req和res对象。我正在摆弄一个解决方案,在所有中间件的末尾放置一个错误中间件来处理域,但我得到了一些奇怪的结果。显然,当您将REQ和RES对象添加到域时,它们将得到与它们相关联的域,即“REQ.DealEnter”属性将被设置。你是对的,退房。我想,总体而言,域代码应该是很好的翻阅(虽然图片中的某些部分是这样的,但它并不太长)。
    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);
    };