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」ファイルの「service」モジュールと同じようにモジュールがオーバーライドされていますよね?.ready() 関数の代わりにコールバックをどこにでも実装する必要があると思いますよね?

4

2 に答える 2

3

ここで3つの問題があります!

  1. readyプロパティを onに設定していますが、オブジェクトserviceは 1 つしかありません。2 つのリクエストが非常に近い場所で発生した場合、最初のリクエストが発生する前に、2 番目のリクエストでプロパティをservice上書きしてしまう可能性があります。これは、オブジェクトreadyのインスタンスが 1 つしかないためです。serviceほぼ確実にそうあるべきなので、心配する必要はありませんが、サービス アクションが完了したことを消費するコードに通知する新しい方法を見つける必要があります。
  2. res内部の変数service.readは、おそらくあなたが思っているものと同じではありませんres。2 回目に呼び出されると、それは異なります。これは、readyプロパティを再定義しているため、毎回異なるスコープを取得しているためです。
  3. ヘッダーを 2 回送信しています。これは簡単な修正です。ヘッダーがどうあるべきかがわかるまで、ヘッダーを送信しないでください。それはすぐ下で明らかになります。

最初の問題を解決するには、コールバックを使用することをお勧めします! ノード コアから既にそれらに精通している必要があります。それらはほぼすべてに使用されます。これにより、JavaScriptのスコープによって、2 番目の問題も修正されます。

注:ファクトリやその他のメソッドの代わりにコールバックを使用することをお勧めする理由は、コールバックがノードランドに普及しているためです。コールバックを使用すると、他のライブラリなどとの統合がはるかに簡単になる可能性が高くなります。 .

サーバーコードを実行する方法は次のとおりです。

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 は最後に取得したリクエストに応答するように設定され、その 1 つだけが応答を取得します。

最善の方法は、次のようなサービスのインスタンスを返す関数をサービス モジュールにエクスポートさせることです。

 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 に答える