Nodejs密集型CPU解决⽅案
⾸先说⼀下nodejs单线程的优势:
⾼性能,与php相⽐,避免了频繁创建切换线程的开销,执⾏更加迅速,资源占⽤⼩。
线程安全,不⽤担⼼同⼀变量被多线程读写,造成程序崩溃。
单线程的异步和⾮阻塞,其实 nodejs底层访问I/O还是多线程的,阻塞/⾮阻塞与异步/同步是两个不同的概念,同步不代表阻塞,但是阻塞肯定就是同步;有点⼉绕⼝,请听我举例,我去⾷堂打饭,我选择了A套餐,然后⼯作⼈员帮我去配餐,如果我就站在旁边,等待⼯作⼈员给我配餐,这种情况就称之为同步;若⼯作⼈员帮我配餐的同时,排在我后⾯的⼈就开始点餐,这样整个⾷堂的点餐服务并没有因为我在等待A套餐⽽停⽌,这种情况就称之为⾮阻塞。这个例⼦就简单说明了同步但⾮阻塞的情况。再如果我在等待配餐的时候去买饮料,等听到叫号再回去拿套餐,此时我的饮料也已经买好,这样我在等待配餐的同时还执⾏了买饮料的任务,叫号就等于执⾏了回调,就是异步⾮阻塞了。如果我在买饮料的时候,已经叫我的号让我去拿套餐,可是我等了好久才拿到饮料,所以我可能在⼤厅叫我的餐号之后很久才拿到A套餐,这也就是单线程的阻塞情况。
多线程:
线程是cpu调度的⼀个基本单位,⼀个cpu只能执⾏⼀个线程任务。
nodejs也可以执⾏多⾏程任务,例如引⽤TAGG/TAGG2模块,⼤家可以看⼀下我上⼀篇⽂章,⾥⾯有具体的使⽤⽅法。但是不论是
tagg/tagg2都是利⽤pthread库和V8::Isolate类来实现js多线程功能的,根据规则我们在线程⾥执⾏的函数⽆法使⽤nodejs的核⼼api,例如fs,crypto模块,所以还是有很⼤的局限性。
多进程:
在⽀持html5的浏览器⾥,我们可以使⽤webworker来将⼀些耗时的计算丢⼊worker进程中执⾏,这样主进程就不会阻塞,⽤户也不会有卡顿的感觉。
这⾥我们需要利⽤nodejs的child_process模块,child_process提供了fork⽅法,可以启动⼀个nodejs⽂件,将它视作worker进程,worker ⼯作完毕后,会把结果send给主进程,然后worker⾃动退出,这样我们就利⽤了多进程解决了主线程阻塞的问题。
var express = require('express');
var fork = require('child_process').fork;
var app = express();
<('/', function(req, res){
var worker = fork('./work.js') //创建⼀个⼯作进程
<('message', function(m) {//接收⼯作进程计算结果
if('object' === typeof m && m.type === 'fibo'){
worker.kill();//发送杀死进程的信号
res.String());//将结果返回客户端
}
});
worker.send({type:'fibo',num:~~req.query.n || 1});
//发送给⼯作进程计算fibo的数量
nodejs字符串转数组
});
app.listen(7878);
我们通过express监听7878端⼝,对每个⽤户的请求都会去fork⼀个⼦进程,通过调⽤worker.send⽅法将参数n传递给⼦进程,同时监听⼦进程发送消息的message事件,将结果响应给客户端。
下⾯是被fork的work.js⽂件内容:
var fibo = function fibo (n) {//定义算法
return n > 1 ? fibo(n - 1) + fibo(n - 2) : 1;
}
<('message', function(m) {
//接收主进程发送过来的消息
if(typeof m === 'object' && m.type === 'fibo'){
var num = fibo(~~m.num);
//计算jibo
process.send({type: 'fibo',result:num})
//计算完毕返回结果
}
});
<('SIGHUP', function() {
});
我们先定义函数fibo⽤来计算斐波那契数组,然后监听了主线程发来的消息,计算完毕之后将结果send到主线程。同时还监听process
的SIGHUP事件,触发此事件就进程退出。
这⾥我们有⼀点需要注意,主线程的kill⽅法并不是真的使⼦进程退出,⽽是会触发⼦进程的SIGHUP事件,真正的退出还是依
靠it()。
总结:
使⽤child_process模块的fork⽅法确实可以让我们很好的解决单线程对cpu密集型任务的阻塞问题,同时⼜没有tagg包那样⽆法使⽤Node.js 核⼼api的限制。
单线程异步的Node.js不代表不会阻塞,在主线程做过多的任务可能会导致主线程的卡死,影响整个程序的性能,所以我们要⾮常⼩⼼的处理⼤量的循环,字符串拼接和浮点运算等cpu密集型任务,合理的利⽤各种技术把任务丢给⼦线程或⼦进程去完成,保持Node.js主线程的畅通。
线程/进程的使⽤并不是没有开销的,尽可能减少创建和销毁线程/进程的次数,可以提升我们系统整体的性能和出错的概率。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。