0

我使用 node.js 制作了一个 REST API 服务,它运行良好,直到我在浏览器中打开几个选项卡并(几乎)同时发出几个请求。发送请求的最后一个选项卡获得响应,另一个挂起。

基本上我有一个名为“服务”的模块,它可以完成所有工作:

var service = require('./service');
var server = http.createServer(function(req, res) {
    ...
    var serviceResult = service.runService(parsedURL.query.username, parsedURL.query.api_key);

    res.writeHead(200, {'content-type': 'application/json', 'connection' : 'keep-alive' });

    service.ready = function(serviceResult) {
        var serviceResultJSON = JSON.stringify(serviceResult);
        res.writeHead(200, {'content-type': 'application/json', 'connection' : 'close' });
        res.end(serviceResultJSON);
    }

}

从我调用的服务模块:

service.ready(result);

...只要结果准备好,简而言之就是我的服务器。那么我能做些什么来解决这个问题呢?

编辑:

这是我的 service.js 模块的样子(在建议的更改之后):

// PUBLIC

exports.runService = function(username, apiKey, callback) {
    _.username              = username;
    _.apiKey                = apiKey;
    init();

    userManager.setLastServiceGlobal(function() {
            // This call triggers the whole cycle. Below is snapshotManager.createdSnapshot(), which gets executed from within snapshotManager and the cycle moves on until apiManager.ready() gets called from within api-manager.js
        snapshotManager.createSnapshot(false);
    });

    // This is the last .ready() function that gets executed when all other modules have finished their job.

    apiManager.ready = function() {
        console.log('API Manager ready.');
        userManager.updateLastService();
        callback(null, serviceResult);
    }
}

// PRIVATE

var userManager             = require('./user-manager'),
    productManager          = require('./product-manager'),
    commentsManager         = require('./comments-manager'),
    apiManager          = require('./api-manager'),
    milestonesManager       = require('./milestones-manager'),
    statisticsManager       = require('./statistics-manager'),
    snapshotManager         = require('./snapshot-manager'),
    serviceResult;

...

snapshotManager.createdSnapshot = function() {
    userManager.refreshUserData();
}
snapshotManager.createdNewSnapshot = function() {
    milestonesManager.generateMilestones();
}
userManager.refreshedUserData = function() {
    userManager.isTimeForPortfolioParse();
}

...

userManager.ready = function() {
    console.log('User Manager ready.');
    userManagerReady = true;
    isDataFetchingOver();
}
productManager.ready = function() {
    console.log('Product Manager ready.');
    productManagerReady = true;
    isDataFetchingOver();
}
commentsManager.ready = function() {
    console.log('Comments Manager ready.');
    commentsManagerReady = true;
    isDataFetchingOver();
}

在这种情况下,模块就像“server.js”文件中的“服务”模块一样被覆盖,对吗?我猜我需要在任何地方实现回调而不是 .ready() 函数,对吗?

4

2 回答 2

3

这里有三个问题!

  1. 您将ready属性设置为service,而只有一个service对象。如果两个请求彼此非常接近,您可能会ready在第一个请求被触发之前用第二个请求覆盖该属性。这是因为该对象只有一个实例service。这几乎肯定是它应该的方式,所以不要担心,但我们必须找到一种新的方式来向您的消费代码发出服务操作已完成的信号。
  2. res里面的变量service.read可能和res你想象的不一样。第二次调用它会有所不同。这是因为您正在重新定义ready属性,因此每次都会获得不同的范围。
  3. 您要发送两次标头。这是一个简单的解决方法——在你知道它们应该是什么之前不要发送任何标题。这将在下面变得明显。

要解决第一个问题,我建议您使用回调!您应该已经从节点核心熟悉它们,它们几乎用于所有事情。由于JavaScript中的作用域,这也恰好解决了第二个问题。

注意:我建议使用回调而不是工厂或其他方法的原因是回调在节点领域普遍存在,所以如果你使用它们,你很有可能更容易与其他库等集成.

这是我如何处理您的服务器代码:

var service = require('./service');
var server = http.createServer(function(req, res) {
  // note that we're not immediately sending headers anymore - this
  // fixes the third problem with what you had before

  service.runService(parsedURL.query.username, parsedURL.query.api_key, function(err, serviceResult) {
    // check for errors! tell the client!
    if (err) {
      res.writeHead(500);
      res.end();
      return;
    }

    res.writeHead(200, {
      'content-type': 'application/json',
      'connection' : 'keep-alive',
    });

    var serviceResultJSON = JSON.stringify(serviceResult);

    res.end(serviceResultJSON);
  });
};

这就是我如何实现这个service东西:

var service = module.exports = {};

service.runService = function runService(username, key, cb) {
  // assume `database` exists from somewhere else...
  database.getUser(username, function(err, user) {
    // make sure we send any errors up the line
    if (err) {
      cb(err);
      return;
    }

    // here's an error we've decided on
    if (user.key !== key) {
      cb(Error("key is incorrect!"));
      return;
    }

    // this is a very common usage pattern - cb(error, result, ...)
    // the reason we're calling this will `null` for the error is a bit
    // of a double negative - we're saying "there was no error".
    cb(null, user);
  });
};
于 2013-06-26T20:45:39.240 回答
0

问题是它们都共享相同的服务对象。因此,当您收到请求时,您会将 service.ready 覆盖为响应该请求的函数。因此,如果您收到 2 个请求非常接近, service.ready 将设置为响应您收到的最后一个请求,因此只有那个请求会得到响应。

最好的方法是让服务模块导出一个返回服务实例的函数,如下所示:

 function serviceFactory() {

  }  

 serviceFactory.prototype.getService() {
    return new Service();
 }
 module.exports = serviceFactory;

然后你可以拥有

var serviceFactory = require(./service);
var server = http.createServer(function(req, res) {
 ...
 var service = serviceFactory.getService();
 var serviceResult = service.runService(parsedURL.query.username, parsedURL.query.api_key);

 res.writeHead(200, {'content-type': 'application/json', 'connection' : 'keep-alive' }); 

 service.ready = function(serviceResult) {
     var serviceResultJSON = JSON.stringify(serviceResult);
     res.writeHead(200, {'content-type': 'application/json', 'connection' : 'close' });
     res.end(serviceResultJSON);
 }
}
于 2013-06-26T20:43:59.223 回答