Fibers 和 Threads
Fibers 称纤程,可以理解为协同程序,类似py和lua都有这样的模型。使用Fibers可以避免对资源的互抢,减少cpu和内存的消耗,但是Fibers并不能够真正的并行执行,同一时刻只有一个Fibers在执行,如果在其中一个Fibers中执行过多的cpu操作或者写了个死循环,则整个主程序将卡死住。node中的异步事件循环模型就有点象这个。
Threads 又称线程,他可以在同一时刻并行的执行,他们共享主进程的内存,在其中某一时刻某一个threads锁死了,是不会影响主线程以及其他线程的执行。但是为了实现这个模型,我们不得不消耗更多的内存和cpu为线程切换的开销,同时也存在可能多个线程对同一内存单元进行读写而造成程序崩溃的问题。
1. cluster
创始人Ryan Dahl建议,运行多个Nodejs进程,利用某些通信机制来协调各项任务。目前,已经有不少第三方的Node.js多进程支持模块发布,而NodeJS 0.6.x 以上的版本提供了一个cluster模块 ,允许创建“共享同一个socket”的一组进程,用来分担负载压力。本篇文章就基于该cluster模块来讲述Node.js在多核CPU下的编程
A single instance of Node.js runs in a single thread. To take advantage of multi-core systems the user will sometimes want to launch a cluster of Node.js processes to handle the load.
The cluster module allows you to easily create child processes that all share server ports.
官网实例
1 const cluster = require('cluster'); 2 const http = require('http'); 3 const numCPUs = require('os').cpus().length; 4 5 if (cluster.isMaster) { 6 // Fork workers. 7 for (var i = 0; i < numCPUs; i++) { 8 cluster.fork(); 9 } 10 11 cluster.on('exit', (worker, code, signal) => { 12 console.log('worker' + worker.id +'died'); 13 }); 14 } else { 15 // Workers can share any TCP connection 16 // In this case it is an HTTP server 17 http.createServer((req, res) => { 18 res.writeHead(200); 19 res.end('hello world '); 20 }).listen(8000); 21 }
这段代码是有主线程 根据cups数目创建子进程,可以根据cluster.work.id打印出是哪个进程处理该请求。
2.node-threads-a-gogo
a.安装
npm install threads_a_gogo
实例
A.- Here's a program that makes Node's event loop spin freely and as fast as possible: it simply prints a dot to the console in each turn:
//cat examples/quickIntro_loop.js
1 (function spinForever () { 2 process.stdout.write("."); 3 process.nextTick(spinForever); 4 })();
B.- Here's another program that adds to the one above a fibonacci(35) call in each turn, a CPU-bound task that takes quite a while to complete and that blocks the event loop making it spin slowly and clumsily. The point is simply to show that you can't put a job like that in the event loop because Node will stop performing properly when its event loop can't spin fast and freely due to a callback/listener/nextTick()ed function that's blocking.
1 //cat examples/quickIntro_blocking.js 2 function fibo (n) { 3 return n > 1 ? fibo(n - 1) + fibo(n - 2) : 1; 4 } 5 6 (function fiboLoop () { 7 process.stdout.write(fibo(35).toString()); 8 process.nextTick(fiboLoop); 9 })(); 10 11 (function spinForever () { 12 process.stdout.write("."); 13 process.nextTick(spinForever); 14 })();
C.- The program below uses threads_a_gogo
to run the fibonacci(35) calls in a background thread, so Node's event loop isn't blocked at all and can spin freely again at full speed:
1 1 //cat examples/quickIntro_oneThread.js 2 2 function fibo (n) { 3 3 return n > 1 ? fibo(n - 1) + fibo(n - 2) : 1; 4 4 } 5 5 6 6 function cb (err, data) { 7 7 process.stdout.write(data); 8 8 this.eval('fibo(35)', cb); 9 9 } 10 10 11 11 var thread= require('threads_a_gogo').create(); 12 12 13 13 thread.eval(fibo).eval('fibo(35)', cb); 14 14 15 15 (function spinForever () { 16 16 process.stdout.write("."); 17 17 process.nextTick(spinForever); 18 18 })();
D.- This example is almost identical to the one above, only that it creates 5 threads instead of one, each running a fibonacci(35) in parallel and in parallel too with Node's event loop that keeps spinning happily at full speed in its own thread:
//cat examples/quickIntro_fiveThreads.js
1 function fibo (n) {
2 return n > 1 ? fibo(n - 1) + fibo(n - 2) : 1; 3 } 4 5 function cb (err, data) { 6 process.stdout.write(" ["+ this.id+ "]"+ data); 7 this.eval('fibo(35)', cb); 8 } 9 10 var threads_a_gogo= require('threads_a_gogo'); 11 12 threads_a_gogo.create().eval(fibo).eval('fibo(35)', cb); 13 threads_a_gogo.create().eval(fibo).eval('fibo(35)', cb); 14 threads_a_gogo.create().eval(fibo).eval('fibo(35)', cb); 15 threads_a_gogo.create().eval(fibo).eval('fibo(35)', cb); 16 threads_a_gogo.create().eval(fibo).eval('fibo(35)', cb); 17 18 (function spinForever () { 19 process.stdout.write("."); 20 process.nextTick(spinForever); 21 })();
E.- The next one asks threads_a_gogo
to create a pool of 10 background threads, instead of creating them manually one by one:
1 //cat examples/multiThread.js 2 function fibo (n) { 3 return n > 1 ? fibo(n - 1) + fibo(n - 2) : 1; 4 } 5 6 var numThreads= 10; 7 var threadPool= require('threads_a_gogo').createPool(numThreads).all.eval(fibo); 8 9 threadPool.all.eval('fibo(35)', function cb (err, data) { 10 process.stdout.write(" ["+ this.id+ "]"+ data); 11 this.eval('fibo(35)', cb); 12 }); 13 14 (function spinForever () { 15 process.stdout.write("."); 16 process.nextTick(spinForever); 17 })();
F.- This is a demo of the threads_a_gogo
eventEmitter API, using one thread:
1 //cat examples/quickIntro_oneThreadEvented.js 2 var thread= require('threads_a_gogo').create(); 3 thread.load(__dirname + '/quickIntro_evented_childThreadCode.js'); 4 5 /* 6 This is the code that's .load()ed into the child/background thread: 7 8 function fibo (n) { 9 return n > 1 ? fibo(n - 1) + fibo(n - 2) : 1; 10 } 11 12 thread.on('giveMeTheFibo', function onGiveMeTheFibo (data) { 13 this.emit('theFiboIs', fibo(+data)); //Emits 'theFiboIs' in the parent/main thread. 14 }); 15 16 */ 17 18 //Emit 'giveMeTheFibo' in the child/background thread. 19 thread.emit('giveMeTheFibo', 35); 20 21 //Listener for the 'theFiboIs' events emitted by the child/background thread. 22 thread.on('theFiboIs', function cb (data) { 23 process.stdout.write(data); 24 this.emit('giveMeTheFibo', 35); 25 }); 26 27 (function spinForever () { 28 process.stdout.write("."); 29 process.nextTick(spinForever); 30 })();
G.- This is a demo of the threads_a_gogo
eventEmitter API, using a pool of threads:
1 //cat examples/quickIntro_multiThreadEvented.js 2 var numThreads= 10; 3 var threadPool= require('threads_a_gogo').createPool(numThreads); 4 threadPool.load(__dirname + '/quickIntro_evented_childThreadCode.js'); 5 6 /* 7 This is the code that's .load()ed into the child/background threads: 8 9 function fibo (n) { 10 return n > 1 ? fibo(n - 1) + fibo(n - 2) : 1; 11 } 12 13 thread.on('giveMeTheFibo', function onGiveMeTheFibo (data) { 14 this.emit('theFiboIs', fibo(+data)); //Emits 'theFiboIs' in the parent/main thread. 15 }); 16 17 */ 18 19 //Emit 'giveMeTheFibo' in all the child/background threads. 20 threadPool.all.emit('giveMeTheFibo', 35); 21 22 //Listener for the 'theFiboIs' events emitted by the child/background threads. 23 threadPool.on('theFiboIs', function cb (data) { 24 process.stdout.write(" ["+ this.id+ "]"+ data); 25 this.emit('giveMeTheFibo', 35); 26 }); 27 28 (function spinForever () { 29 process.stdout.write("."); 30 process.nextTick(spinForever); 31 })();