浅析Node.js遇到cpu密集型计算如何优化

首先看下面这段代码

setTimeout(function () {
for (var i = 0; i < 10000000000; i++) {
//CPU密集
}
}, 200);
setTimeout(function () {
console.log('210 ms...');
}, 210);

这个小例子很经典的解释了Node.js遇到密集型CPU的时候问题,这个程序分别在两个时间点触发,虽然在210ms得时候回调内得程序执行非常很快,但是在200ms得时候处理了一个CPU非常密集型的任务就导致整个线程阻塞了.
至于Event Loop机制可以参考:
http://www.infoq.com/cn/articles/nodejs-weakness-cpu-intensive-tasks
这篇文章中关于Event Loop和Tick的一段:

每个Node程序的主线程都有一个event loop,JavaScript代码全在这个单线程下运行。所有的I/O操作以及对本地API的调用,或者是异步的(借助程序所在平台的机制),或者运行在另外的线程中。这全都是通过libuv处理的。所以当socket上有数据过来,或本地API函数返回时,需要有种同步的方式调用对刚发生的这一特定事件感兴趣的JavaScript函数。

在发生事件的线程中直接调用JS函数是不安全的,因为那样也会遇到常规多线程程序遇到的问题,竞态条件、非原子操作的内存访问等等。所以要以一种线程安全的方式把事件放在队列中,如果写成代码,大致应该是这样的:

lock (queue) {
queue.push(event);
}

然后在执行JavaScript的主线程中(即event loop的c代码):

while (true) {
// tick开始
lock (queue) {
var tickEvents = copy(queue);
// 将当前队列中的条目复制的线程自有的内存中
queue.empty(); // ..清空共享的队列
}
for (var i = 0; i < tickEvents.length; i++) {
InvokeJSFunction(tickEvents[i]);
}
// tick结束
}

while (true) (在真正的node源码中并不是这样的;这里只是为了说明)表示event loop。里面的for为队列中的每个事件调用JS函数。Event loop在每个tick中都会调用与外部事件相关联的零个或多个回调函数,一旦队列被清空,并且最后一个函数返回后,tick就结束了。然后回到开始(下一个tick),重新开始检查其它线程在JavaScript运行时加到队列中的事件。

那么这个队列中的东西都是谁放进来的呢?

process.nextTick
setTimeout/setInterval
I/O (来自fs、net等)
crypto中的CPU密集型函数,比如crypto streams、pbkdf2和PRNG
所有使用libuv工作队列异步调用C/C++库的本地模块

同样下面代码当调用t1接口时也会导致t2的接口变慢.

app.get("/t1", function* (next) {
for (var i=0; i<5000000000; i++) {
//cpu密集型
}
console.log("t1");
});
app.get("/t2", function* (next) {
console.log("t2");
});

这么看Node.js是否能够胜任cpu密集型操作呢,答案当然是否定的,Node.js虽然是单线程,但是可以开启多个Node.js实例来充分利用多核的优势,另外Node.js还支持子进程,通过子进程来计算…

for.js

var calc = function() {
for (var i = 0; i < 10000000000; i++) {
}
}
process.on('message', function(m) {
//接收主线程发来消息
console.log("recv mesage");
calc();
process.send(1);
});
process.on('SIGHUP', function() {
process.exit();//收到kill信息,进程退出
});

main.js

var fork = require('child_process').fork;
setTimeout(function () {
var worker = fork("./for.js");
worker.on("message", function(m) {
//接收工作进程的结果
console.log("world");
worker.kill();
});
worker.send(1);
}, 200);
setTimeout(function () {
console.log('hello');
}, 210);

上面这个程序就是利用进程间来通信,for.js是子进程执行的密集型计算.main.js可以继续调度其他程序.充分利用了cpu.

时间: 2024-10-23 03:49:26

浅析Node.js遇到cpu密集型计算如何优化的相关文章

浅析Node.js中使用依赖注入的相关问题及解决方法

这篇文章主要介绍了浅析Node.js中使用依赖注入的相关问题及解决方法,Node.js是一个将JavaScript应用运行于服务器端的框架,需要的朋友可以参考下 最近,我转向使用依赖注入来帮助理解分离代码的简单途径,并有助测试.然而,Node.js中的模块依赖Node提供的系统API,这很难判断私有依赖被恰当的使用.一般的依赖注入很难在这种情况下使用,但现在不要放弃希望. requireCauses 问题 Node.js很容易依照需求导入依赖.它运行的很好,并且比AMD模式加载器例如Requir

浅析Node.js中的内存泄漏问题

  这篇文章是由Mozilla的Identity团队带来的 A Node.JS Holiday Season系列文章的首篇,该团队上个月发布了 Persona的第一个测试版本.在开发Persona时我们构建了一系列的工具,包括了从调试,到本地化,到依赖管理以及更多的方面.在这一系列的文章中我们将与社区分享我们的经验和这些工具,这对任何想用node.js建立一个高可用性服务的人都很有用.我们希望您能喜欢这些文章,并期待看到您的想法和贡献. 我们将从一篇关于Node.js的实质性问题:内存泄漏的主题

深入浅析Node.js 事件循环_node.js

Node.js 是单进程单线程应用程序,但是通过事件和回调支持并发,所以性能非常高. (来源于Javascript是单线程又是异步的,但是这种语言有个共同的特点:它们是 event-driven 的.驱动它们的 event 来自一个异构的平台.) Node.js 的每一个 API 都是异步的,并作为一个独立线程运行,使用异步函数调用,并处理并发. Node.js 基本上所有的事件机制都是用设计模式中观察者模式实现. Node.js 单线程类似进入一个while(true)的事件循环,直到没有事件

浅析Node.js 中 Stream API 的使用_node.js

本文由浅入深给大家介绍node.js stream api,具体详情请看下文吧. 基本介绍 在 Node.js 中,读取文件的方式有两种,一种是用 fs.readFile ,另外一种是利用 fs.createReadStream 来读取. fs.readFile 对于每个 Node.js 使用者来说最熟悉不过了,简单易懂,很好上手.但它的缺点是会先将数据全部读入内存,一旦遇到大文件的时候,这种方式读取的效率就非常低下了. 而 fs.createReadStream 则是通过 Stream 来读取

浅析Node.js的Stream模块中的Readable对象_node.js

我一直都很不愿意扯 nodejs 的流,因为从第一次看到它我就觉得它的设计实在是太恶心了.但是没办法,Stream 规范尚未普及,而且确实有很多东西都依赖了 nodejs 的流来实现的,所以我也只能捏着鼻子硬着头皮来扯一扯这又臭又硬的 nodejs 流对象了. nodejs 自带了一个叫 stream 的模块,引入它便可以得到一组流对象构造器.现在我只说最简单的 stream.Readable. 其实用过 nodejs 的几乎都接触过 Readable 的实例,只是平时没太在意而已.一个非常典型

浅析Node.js中的内存泄漏问题_node.js

 这篇文章是由Mozilla的Identity团队带来的 A Node.JS Holiday Season系列文章的首篇,该团队上个月发布了 Persona的第一个测试版本.在开发Persona时我们构建了一系列的工具,包括了从调试,到本地化,到依赖管理以及更多的方面.在这一系列的文章中我们将与社区分享我们的经验和这些工具,这对任何想用node.js建立一个高可用性服务的人都很有用.我们希望您能喜欢这些文章,并期待看到您的想法和贡献. 我们将从一篇关于Node.js的实质性问题:内存泄漏的主题文

浅析Node.js查找字符串功能_node.js

需求如下: 整个目录下大概有40几M,文件无数,由于时间久了, 记不清那个字符串具体在哪个文件,于是.强大,亮瞎双眼的Node.js闪亮登场: windows下安装Node.js和安装普通软件毫无差别,装完后打开Node.js的快捷方式,或者直接cmd,你懂的. 创建findString.js var path = require("path"); var fs = require("fs"); var filePath = process.argv[2]; var

浅析Node.js实现HTTP文件下载_node.js

前言 HTTP实现文件下载时,只要在服务器设置好相关响应头,并使用二进制传输文件数据即可,而客户端(浏览器)会根据响应头接收文件数据.而在Node.js中,设置好响应头后,读取文件流,再使用".pipe()"方法将流转接到响应对象Response就可以实现一个简单的文件下载服务器. 1. 文件下载介绍 HTTP基于请求头和响应头实现状态交互,在得到服务器正确响应状态后,而客户端首先会解析响应头,并根据响应头来接收和展示数据(响应体).对于文件下载来说,其实现过程如下:     1.客户

浅析Node.js的Modules模块载入方式与机制

Node.js中模块可以通过文件路径或名字获取模块的引用.模块的引用会映射到一个js文件路径,除非它是一个Node内置模块.Node的内置模块公开了一些常用的API给开发者,并且它们在Node进程开始的时候就预加载了. 其它的如通过NPM安装的第三方模块(third-party modules)或本地模块(local modules),每个模块都会暴露一个公开的API.以便开发者可以导入.如 Js代码   var mod = require('module_name') 此句执行后,Node内部