2

私は API を書いていますが、着信要求に応じて非同期コードと同期コードを混在させているところで行き詰まりました。以下の例を見てください。

ルート.js

module.exports = [
    {
        method: 'GET',
        path: '/',
        controller: 'main',
        action: 'main',
        description: 'lists the API functionality',
        access: 'auth'
    },
    {
        method: 'POST',
        path: '/users',
        controller: 'users',
        action: 'create',
        description: 'creates a new user',
        fields: {
            fullName: {
                format: {
                    min: 2,
                    max: 64,
                    minWords: 2,
                    disableDoubleSpaces: true
                },
                description: 'the full name of the new user',
                examples: 'Thomas Richards, Richard Jones, Michael J. Fox, Mike Vercoelen, John Johnson'
            },
            email: {
                format: {
                    min: 2,
                    max: 64,
                    maxWords: 1,
                    match: 'email'
                },
                description: 'the email address of the new user',
                examples: 'mike@grubt.com, lilly@gmail.com, thomas.richards@mail.com, peter@mymail.com'
            },
            password: {
                format: {
                    min: 2,
                    max: 64
                },
                description: 'the password of the new user',
                examples: '123abcdfg, 7373kEjQjd, #8klKDNfk'
            }
        }
    }
];

routes.js ファイルは基本的に API の非常に重要な部分であり、受信データを検証し、正しいコントローラー/アクションにルーティングし、メソッドがパブリックかどうか、または認証 (基本認証) が必要かどうかを定義します。

api-server.js

var http = require('http');
var url = require('url');
var os = require('os');
var dns = require('dns');

var apiServer = module.exports;
var routes = require('./routes.js'); // Routes file from above.

var req, res, controller, action, serverInfo, httpServer;

apiServer.start = function(){
  prepare(function(){
    httpServer = http.createServer(handleRequest).listen(3000);
  });
};

//
// We need to do this function, we need the local ip address of the 
// server. We use this local ip address in logs (mongoDb) so we can
// refer to the correct server.
//
function prepare(callback){
  var serverName = os.hostname();

  dns.lookup(serverName, function(error, address){
    if(error){
      throw error;
    }

    serverInfo = {
      name: serverName,
      address: address
    };

    callback();
  });
}

function getRoute(){
  // loops through routes array, and picks the correct one...
}

function getAuth(callback){
  // parses headers, async authentication (mongoDB).
}

function getRequestData(callback){
  // req.on('data') and req.on('end'), getting request data.
}

function parseRequestData(callback){
  // parse request data at this point.
}

function validateRequestData(callback){
  // loop through route fields (see routes.js) and validate this data with the ones
  // from the request.
}

function requireControllerAndCallAction(){
  // do actual job.
}

function handleRequest(request, response){
  req = request;
  res = response;

  req.route = getRoute(); // First step for a request, syncronous.

  if(req.route === false){
    // 404...
  }

  // If in the routing schema access was "auth", 
  // this route requires authentication, so do that...
  if(req.route.access === 'auth'){
    getAuth(function(error, user){
      if(error){ // 401 } else {
        req.user = user;
      }
    }
  }

  if(req.method === 'POST' || req.method === 'PUT'){

    // Async functions.
    getRequestData(function(){
      parseRequestData(function(){
        validateRequestData(function(){
          requireControllerAndCallAction();
        });
      });
    });
  } else {
    requireControllerAndCallAction();
  }
}

ご覧のとおり、一部の関数は非同期 (getAuth、getRequestData) であり、一部は同期 (parseRequestData、validateRequestData) です。

今ここにあります:

リクエスト 1. POST メソッド、url '/users'、およびデータが含まれています。

  • fullName = 'リック'
  • 電子メール: 'rick@'
  • パスワード: 'a'

したがって、API のワークフローをループします。

  1. 現在のルートを取得します (コントローラー: ユーザー、アクション: 作成) routes.js の 2 番目の配列要素を参照してください

  2. 要求データを取得し、コールバックで次のことを行います。データを解析する b. データの検証

ここで想像してみましょう。データの検証には 5 秒かかります (これは遅れますが、例にすぎません)。その検証中に新しい要求が着信し、新しい要求は前の要求が完了するまで処理されません。

4

1 に答える 1

6

それらが同期していて、5 秒かかった場合、はい、サーバーのそのインスタンスに対して、要求はブロックされます。そのため、ブロック呼び出し (ネットワーク、データベース、ファイル システムなど) 呼び出しが非同期であることが重要です。イベント ループがループし続けるか、サーバー全体がブロックされる必要があります。

http://blog.mixu.net/2011/02/01/understanding-the-node-js-event-loop/

その記事の重要な行は次のとおりです。

…ただし、コード以外はすべて並行して実行されます

つまり、高価な I/O は非同期にする必要がありますが、コードがブロックされる可能性があります。しかし、サーバー上でのブロックが心配されるのは、通常、高価な I/O です。

「あなたのコード」は通常、実行時間の長い I/O 呼び出しからのコールバックを処理し、状態を更新してから別の呼び出しを開始するだけです。しかし、それはそれの美しさです。状態を更新するとき、それはメイン イベント ループ上にあるため、「自分の」状態とコードへのマルチスレッド アクセスは必要ありません。ロックやデッドロックなどはありませんが、高価な部分である非同期および並列 I/O のすべての利点があります。

もう 1 つの重要なポイント (5 秒間の非 IO 作業が該当します) は次のとおりです。

I/O 呼び出しを除いて、Node.js はすべての要求が迅速に返されることを期待しています。たとえば、CPU を集中的に使用する作業は、イベントのようにやり取りできる別のプロセスに分割するか、WebWorkers のような抽象化を使用する必要があります。

また、「auth」と「POST」を期待してください | 同じリクエストで「PUT」が発生することはありますか? その場合、問題がある可能性があります。getAuth は非同期のように見えますが、すぐに req.method チェックに進みます。その時点で、getAuth は引き続き機能します。POST と PUT を認証する場合は、非同期メソッドのブロックを getAuth 非同期メソッドでラップする必要がある場合があります。

于 2012-08-21T10:39:28.830 に答える