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' 和数据:

  • 全名 = '瑞克'
  • 电子邮件:'rick@'
  • 密码:'a'

所以我们循环遍历 API 的工作流程:

  1. 获取当前路由(控制器:用户,操作:创建) 查看 routes.js 中的第二个数组元素

  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 的所有好处是昂贵的部分。

另一个关键点(5 秒的非 IO 工作会落入其中)是:

除了 I/O 调用,Node.js 期望所有请求都能快速返回;例如,CPU 密集型工作应该被拆分到另一个进程,您可以与该进程进行事件交互,或者使用 WebWorkers 之类的抽象。

另外,不要期待'auth'和'POST' | 'PUT' 是否会在同一个请求中发生?如果是这样,您可能会遇到问题。getAuth 看起来是异步的,但随后您立即转到 req.method 检查。那时,getAuth 仍然可以工作。如果您希望对 POST 和 PUT 进行身份验证,则可能需要使用 getAuth 异步方法包装该异步方法块。

于 2012-08-21T10:39:28.830 回答