NodeJs如何创建非阻塞计算

Posted

技术标签:

【中文标题】NodeJs如何创建非阻塞计算【英文标题】:NodeJs how to create a non-blocking computation 【发布时间】:2012-03-21 17:15:55 【问题描述】:

我正试图在 nodejs 中创建一个非阻塞的繁重计算。举个例子(去掉其他东西):

http.createServer(function(req, res) 
    console.log(req.url);
    sleep(10000);
    res.end('Hello World');
).listen(8080, function()  console.log("ready"); );

你可以想象,如果我同时打开 2 个浏览器窗口,第一个将等待 10 秒,另一个将等待 20 秒,正如预期的那样。因此,知道回调在某种程度上是异步的,我删除了睡眠并改为:

doHeavyStuff(function() 
    res.end('Hello World');
);

函数定义简单:

function doHeavyStuff(callback) 
    sleep(10000);
    callback();

那当然行不通...我也尝试定义一个 EventEmitter 并注册到它,但是 Emitter 的主要功能在发出“完成”之前在里面有睡眠,例如,所以一切都会再次运行块。

我想知道其他人是如何编写非阻塞代码的...例如 mongojs 模块或 child_process.exec 是非阻塞的,这意味着在代码中的某个地方,他们要么在另一个线程上分叉一个进程,要么听它的事件。我怎样才能在一个例如有很长过程的方法中复制它?

我是否完全误解了 nodejs 范式? :/

谢谢!

更新:解决方案(有点)

感谢 Linus 的回答,确实唯一的方法是产生一个子进程,例如另一个节点脚本:

http.createServer(function(req, res) 
    console.log(req.url);

    var child = exec('node calculate.js', function (err, strout, strerr) 
        console.log("fatto");
        res.end(strout);
    );

).listen(8080, function()  console.log("ready"); );

calculate.js 可以花时间做它需要做的事情并返回。这样,可以说多个请求会并行运行。

【问题讨论】:

【参考方案1】:

虽然这是一篇旧帖子(8 年前),但请尝试添加一些新的更新。

    要让 Nodejs 应用程序获得良好的性能,第一要务绝不是阻塞事件循环sleep(10000) 方法打破了这个规则。这也是 Node.js 不适合 CPU 密集型应用的原因。由于大 CPU 计算发生在事件循环线程上(它也是 node.js 的主线程和单线程)并且会阻塞它。

    多线程编程work_threads从第12版开始被引入node.js生态系统。与多进程编程相比,它更轻量级,开销更小。

    虽然node.js中引入了多线程,但是Node.js还是基于事件驱动模型和异步非阻塞IO。这就是 node.js 的 DNA。

【讨论】:

【参考方案2】:

我们(Microsoft)刚刚发布了napajs,它可以与 Node.js 一起使用,以在同一进程中启用多线程 javascript 场景。

您的代码将如下所示:

var napa = require('napajs');

// One-time setup. 
// You can change number of workers per your requirement. 
var zone = napa.zone.create('request-worker-pool',  workers: 4 );

http.createServer(function(req, res) 
    console.log(req.url);

    zone.execute((request) => 
        var result = null;
        // Do heavy computation to get result from request
        // ...
        return result;
    , [req]).then((result) => 
        res.end(result.value);
    
).listen(8080, function()  console.log("ready"); );

您可以阅读this post了解更多详情。

【讨论】:

napajs 的发展现状如何?取消了吗?还有其他选择吗?【参考方案3】:

这是对事件循环如何工作的典型误解。

这不是节点独有的——如果你在浏览器中有一个长时间运行的计算,它也会阻塞。做到这一点的方法是将计算分解成小块,这些小块产生事件循环的执行,允许 JS 环境与其他竞争调用交错,但只有一件事发生在一个时间。

setImmediate 演示可能具有指导意义,您可以找到 here。

【讨论】:

我对这个答案的问题是您建议在事件循环中运行计算。与阻塞 IO 调用一样,阻塞计算调用应该在事件循环外部运行,并且使用回调策略来处理阻塞计算与阻塞 IO 调用没有区别。你给它输入并释放它,当它完成后,它会用它的输出给你回电。现在对于浏览器来说,你没有可用的底层线程池,像 setImmediate 这样的库是至关重要的,但是,在浏览器上进行大量计算是例外而不是规则 不是手动处理屈服,而是产生一个子进程允许操作系统自动为您处理这个并且可能更有效。 (如果 CPU 上有额外的内核可用,它也会使用它。)这使您的应用程序代码可以简化为仅消息传递。对于浏览器,您可以使用 Web Worker API。【参考方案4】:

如果您的计算可以分成块,您可以安排执行程序每 N 秒轮询一次数据,然后在 M 秒后再次运行。或者单独为该任务生成专用子线程,这样主线程就不会阻塞。

【讨论】:

【参考方案5】:

如果不使用 node 中的某些 IO 模块(例如 fsnet),您无法直接执行此操作。如果您需要进行长时间运行的计算,我建议您在子进程(例如child_process.fork)或队列中进行。

【讨论】:

child_process 没有 fork() 方法,只有 exec 和 spawn。但你是对的,这是避免阻塞一切的唯一方法...... 给你:child_process.fork。特别是,fork 允许与子进程通信。 是的,对...我想我坐在旧版本的文档上却没有意识到... :)

以上是关于NodeJs如何创建非阻塞计算的主要内容,如果未能解决你的问题,请参考以下文章

NodeJs如何创建非阻塞计算

NodeJs如何创建非阻塞计算

NodeJS 阻塞/非阻塞

为啥说nodejs是异步非阻塞

nodejs的异步非阻塞IO

nodejs真的是单线程吗?