【问题标题】:How to make node works concurrently?如何使节点同时工作?
【发布时间】:2014-03-21 11:06:11
【问题描述】:

Node.js 以并发而闻名,但是,我对如何使其同时工作感到困惑。我非常快速地从 Chrome 中一一启动了两个请求,我预计控制台中的输出应该是:

  1. “获取新请求”
  2. 在我的第二个请求之后,应立即打印“get a new request”
  3. 几秒钟后,“结束新请求”
  4. 几秒钟后,“结束新请求”

然而,我看到的是:

  1. “获取新请求”
  2. 几秒钟后,“结束新请求”
  3. “获取新请求”
  4. 几秒钟后,结束新请求

这意味着在第一个请求完成之前不会处理第二个请求。下面是我的示例代码,有什么遗漏的吗?

   var http = require("http");
   var url = require("url");

   function start(route) {
    http.createServer(function(request, response) {
      console.log('get a new request');

      // a time consuming loop           
      for (var i=0; i<10000000000; ++i) {
      }

      route(url.parse(request.url).pathname);
      response.writeHead(200, {"Content-Type": "text/plain"});
      response.end();
      console.log('end the new request');
    }).listen(5858);
   }

   function saySomething(something)  {
     console.log(something);
   }

   exports.start = start;
   exports.saySomething = saySomething;

【问题讨论】:

标签: node.js concurrency


【解决方案1】:

【讨论】:

  • 他的代码是同步运行一个循环,相信作者想知道如何写一个异步延迟:)你应该建议一个setTimeout(callback, timeout):P
  • 埃尔夫。真的。我可能过于热心了。
【解决方案2】:

节点无法处理这些:

for (var i=0; i<10000000000; ++i) {}

同时。但它同时处理 IO

【讨论】:

    【解决方案3】:

    你不需要做任何事情。

    它基于非阻塞 I/O。简而言之,有一个事件循环。运行一组同步代码,一旦完成,下一次迭代就会运行,它会选择下一组同步代码来运行。任何时候运行异步操作(db fetch、setTimeout、读取文件等),都会运行事件循环的下一个滴答声。这样就永远不会有任何代码在等待。

    它没有线程。在您的示例中,for 循环位于一个连续的代码块中,因此 js 将运行整个 for 循环,然后才能处理另一个 http 请求。

    尝试在 for 循环周围放置一个 setTimeout,以便节点可以切换到下一个事件循环并在您的情况下处理 Web 请求。

    【讨论】:

    • 所以,比如事件循环中有10个请求,第一个从循环中出队,由服务器处理,如果有一些耗时的I/O操作,第一个请求将进入睡眠状态,直到 I/O 完成并排队到事件循环的末尾。因为第一个请求再次排队,第二个请求就有机会被处理,对吗?
    • 这与操作需要多长时间无关。那是由你决定的部分。当每个代码都延迟执行时,会运行另一个事件循环。
    • 因此,如果您知道您有一个昂贵的功能,您可以将其分批并推迟每个组。最简单的方法是使用 setTimeout。
    • 当您创建任何 io 时,您指定的处理程序将在未来的滴答声中运行。节点不等待 io 完成。
    • setTimeout 放在循环中不会改变任何东西;它在运行时仍会阻止所有其他事件。减少阻塞的唯一方法是将循环分成多个批次,在每个批次之间使用setImmediate,或者生成一个新进程(Worker)。同样,setTimeout(fn, 0)process.nextTick 都不起作用,因为它们不会屈服于事件循环。
    【解决方案4】:

    这是预期的行为,̶我们称这种̶̶b̶l̶o̶c̶k̶i̶n̶g̶,该溶液用于处理并发请求是使代码̶̶n̶o̶n̶-̶b̶l̶o̶c̶k̶i̶n̶g̶,一旦你打电话̶̶r̶e̶s̶p̶o̶n̶s̶e̶.̶w̶r̶i̶t̶e̶H̶e̶a̶d̶̶代码开始̶b̶l̶o̶c̶k̶i̶n̶g̶̶w̶a̶i̶t̶i̶n̶g̶̶f̶o̶r̶̶̶r̶e̶s̶p̶o̶n̶s̶e̶.̶e̶n̶d̶̶.

    编辑 7/8/14: 最近不得不处理这个问题,发现你可以为此使用线程: https://www.npmjs.org/package/webworker-threads

    Webworker-threads 为 Node.js 中缺少的 CPU 密集型任务提供异步 API:

    var Worker = require('webworker-threads').Worker;
    require('http').createServer(function (req,res) {
      var fibo = new Worker(function() {
        function fibo (n) {
          return n > 1 ? fibo(n - 1) + fibo(n - 2) : 1;
        }
        this.onmessage = function (event) {
          postMessage(fibo(event.data));
        }
      });
      fibo.onmessage = function (event) {
        res.end('fib(40) = ' + event.data);
      };
      fibo.postMessage(40);
    }).listen(port);
    

    它不会阻塞事件循环,因为对于每个请求,fibo worker 将在单独的后台线程中并行运行。

    【讨论】:

    • 感谢您的解释,但是,您的解决方案无法解决我的问题,输出顺序没有改变。正如pbo 所说,setTimeout 可以解决问题。
    • setTimeout 或函数都可以在这些情况下工作,问题是您希望代码直接使用函数执行还是使用 setTimeout 延迟执行。关键是阻塞代码应该在不同的事件循环中,无论是通过 setTimeout 还是函数。
    • 我不明白...为什么函数 timeConsumingLoop 可以推迟繁忙循环的执行?我试过你的代码,输出是:get/end、get/end、get/end...
    • @buaaji 我已经更新了我的答案,我遇到了一个我最近不得不使用的解决方案。
    • @MDG 这是可能的,但不是一个好主意:它会在重负载下消耗大量内存,线程不是轻量级的(因为隔离)。你不能共享任何东西(=没有闭包),函数实际上是通过toString 传递的,也没有require 和其他节点api。将一堆节点进程设置为 worker 并使用 0mq 之类的东西分配作业会更容易。
    猜你喜欢
    • 2019-03-13
    • 1970-01-01
    • 1970-01-01
    • 2019-11-21
    • 2015-03-18
    • 1970-01-01
    • 1970-01-01
    • 2020-06-03
    • 1970-01-01
    相关资源
    最近更新 更多