【问题标题】:Parallel Request at different paths in NodeJS: long running path 1 is blocking other pathsNodeJS中不同路径的并行请求:长时间运行的路径1阻塞了其他路径
【发布时间】:2014-07-06 08:24:27
【问题描述】:

我正在尝试简单的 NodeJS 应用程序,以便了解异步性质。

但是我的问题是,一旦我从浏览器中点击“/home”,它就会等待响应,同时当“/”被点击时,它会先等待“/home”的响应,然后再响应到“/”请求。

我担心的是,如果其中一个请求需要繁重的处理,我们不能同时请求另一个请求吗?这是正确的吗?

    app.get("/", function(request, response) {
        console.log("/ invoked");
        response.writeHead(200, {'Content-Type' : 'text/plain'});
        response.write('Logged in! Welcome!');
        response.end();
    });

    app.get("/home", function(request, response) {
        console.log("/home invoked");
        var obj = {
            "fname" : "Dead",
            "lname" : "Pool"
        }
        for (var i = 0; i < 999999999; i++) {
            for (var i = 0; i < 2; i++) {
                // BS
            };  
        };
        response.writeHead(200, {'Content-Type' : 'application/json'});
        response.write(JSON.stringify(obj));
        response.end();
    });

【问题讨论】:

  • 您可能不会在代码中运行 10 亿次迭代循环。想一想:是否有可能以异步方式实现“/home”中的处理?是的!您可以使用在该处理程序中完成的工作来扩展您的任务。 Async 帮助组织代码。
  • 我想检查是否有任何请求以某种方式被阻止或多个数据库查询等可能延迟响应,也会影响其他请求!那只是悲伤。看来我必须在我的方法中添加Async
  • 数据库查询 !== 十亿次迭代循环

标签: node.js


【解决方案1】:

好问题, 现在,虽然 Node.js 具有异步特性,但这段代码:

for (var i = 0; i < 999999999; i++) {
    for (var i = 0; i < 2; i++) {
        // BS
    };  
};

不是异步的实际上阻塞了节点主线程。因此,所有其他请求都必须等到这个 for 大循环结束。

为了并行进行一些繁重的计算,我建议使用setTimeoutsetInterval 来实现您的目标:

var i=0;
var interval = setInterval(function() {
   if(i++>=999999999){
       clearInterval(interval);
   }
   //do stuff here
},5);

有关更多信息,我建议搜索“Node.js 事件循环”

【讨论】:

    【解决方案2】:

    作为Staselstated,运行类似的代码会阻塞事件循环。基本上,只要 javascript 在服务器上运行,就没有其他任何东西在运行。磁盘 I/O 等异步 I/O 事件可能正在后台处理,但除非您的同步代码完成运行,否则不会调用它们的处理程序/回调。基本上,一旦完成,节点就会检查待处理的事件并分别调用它们的处理程序。

    你实际上有几个选择来解决这个问题。

    1. 将工作分成几部分,让待处理的事件在其间执行。这与 Stasel 的建议几乎相同,只是单次迭代之间的 5 毫秒是巨大的。对于像 999999999 项这样的东西,这需要很长时间。首先,我建议在一段时间内对循环进行批处理,然后使用setimmediate 安排下一个批处理。 setimmediate 基本上会在处理完待处理的 I/O 事件后进行调度,所以如果没有新的 I/O 事件需要处理(比如没有新的 http 请求),它将立即执行。它足够快。现在问题来了,我们应该为每个批次/迭代做多少处理。我建议首先手动测量平均多少,并安排大约 50 毫秒的工作。例如,如果您已经意识到 1000 个项目需要 100 毫秒。然后让它处理 500 个项目,所以它是 50ms。您可以进一步分解它,但分解得越多,总花费的时间就越多。所以要小心。此外,由于您正在处理大量项目,请尽量不要制造太多垃圾,这样垃圾收集器就不会阻塞太多。在this not-so-similar question 中,我已经解释了如何在不阻塞事件循环的情况下将 10000 个文档插入 MongoDB。

    2. 使用线程。实际上有几个不错的线程实现,您不会与它们步履蹒跚。对于这种情况,这确实是一个好主意,如果您正在寻找大型处理的性能,因为正如我上面所说的那样,实现 CPU 绑定任务与同一进程中发生的其他事情很好地配合会很棘手,异步事件非常适合数据绑定任务不是 CPU 绑定任务。您可以使用nodejs-threads-a-gogo 模块。你也可以使用node-webworker-threads,它建立在threads-a-gogo上,但是带有webworker API。还有nPool,它看起来更漂亮但不太受欢迎。它们都支持线程池,应该可以直接实现工作队列。

    3. 创建多个进程而不是线程。这可能比线程慢,但对于巨大的东西仍然比在主进程中迭代要好得多。有不同的方法。使用进程将为您带来一种设计,您可以将其扩展到使用多台机器,而不仅仅是使用多个 CPU。您可以使用作业队列(基本上每当完成要处理的任务时从队列中拉下一个)、多进程 map-reduce 或 AWS 弹性 map reduce,或者使用 nodejs 集群模块。使用集群模块,您可以在每个工作人员上侦听 unix 域套接字,并且对于每个作业,只需向该套接字发出请求。每当工作人员完成处理工作时,它只会写回该特定请求。您可以搜索这些东西,已经存在许多实现和模块。您可以使用 0MQ、rabbitMQ、node 内置 ipc、unix 域套接字或 redis 队列进行多进程通信。

    【讨论】:

      猜你喜欢
      • 2014-12-15
      • 1970-01-01
      • 2019-04-08
      • 1970-01-01
      • 1970-01-01
      • 2021-07-09
      • 2018-01-03
      • 1970-01-01
      • 2020-04-01
      相关资源
      最近更新 更多