从笔记(14)就提及多进程,今日总算可以进入正题。
从v0.6.x开始,Node.js提供了多进程模块cluster,允许创建一组进程来共享同一个socket,并且分担负载压力。
官方文档是这样说的:
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.
单个node.js实例的运行是单线程的。用户有时候想运行一组进程来运行node.js实例以提高多核系统的性能。
cluster模块让你轻松创建可以共享服务端口的子进程。
那么试试先,用cluster共享一个http服务。
cluster-1.js:
const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;
if (cluster.isMaster) {
// Fork workers.
for (var i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on('exit', function(worker, code, signal){
console.log('worker ' + worker.process.pid +' died');
});
} else {
// Workers can share any TCP connection
// In this case it is an HTTP server
http.createServer(function(req, res){
res.writeHead(200);
res.end('hello world, child-process:'+cluster.worker.id+'\n');
}).listen(8000);
}
开启服务:
lee@mypc ~/works/nodejs/study17 $ node cluster-1.js
http访问效果:
lee@mypc ~ $ curl http://localhost:8000
hello world, child-process:2
lee@mypc ~ $ curl http://localhost:8000
hello world, child-process:3
lee@mypc ~ $ curl http://localhost:8000
hello world, child-process:1
lee@mypc ~ $ curl http://localhost:8000
hello world, child-process:4
lee@mypc ~ $ curl http://localhost:8000
hello world, child-process:2
lee@mypc ~ $ curl http://localhost:8000
hello world, child-process:3
lee@mypc ~ $ curl http://localhost:8000
hello world, child-process:1
lee@mypc ~ $ curl http://localhost:8000
hello world, child-process:4
lee@mypc ~ $ curl http://localhost:8000
hello world, child-process:2
lee@mypc ~ $ curl http://localhost:8000
hello world, child-process:3
显然,虽然每次http访问都能得到结果“hello world”,但却并不是同一个进程。
并且我们可以看到cluster实现了的负载均衡,cluster会把请求依次分配给子进程2-3-1-4-2-3-1-4...........这是一个罗宾环。
cluster默认使用round-robin来实现负载均衡。也可以不使用round-robin。
lee@mypc ~/works/nodejs/study17 $ env NODE_CLUSTER_SCHED_POLICY="none" node cluster-1.js
lee@mypc ~ $ curl http://localhost:8000
hello world, child-process:4
lee@mypc ~ $ curl http://localhost:8000
hello world, child-process:1
lee@mypc ~ $ curl http://localhost:8000
hello world, child-process:1
lee@mypc ~ $ curl http://localhost:8000
hello world, child-process:1
lee@mypc ~ $ curl http://localhost:8000
hello world, child-process:1
lee@mypc ~ $ curl http://localhost:8000
hello world, child-process:4
lee@mypc ~ $ curl http://localhost:8000
hello world, child-process:3
lee@mypc ~ $ curl http://localhost:8000
hello world, child-process:1
cluster是如何工作的?
cluster的工作进程是通过child_process.fork()来创建的,所以它们与父进程间可以进行通讯。
可以从源码中找到child_process.fork()的实现。
源码片段:
'use strict';
const EventEmitter = require('events');
const assert = require('assert');
const dgram = require('dgram');
const fork = require('child_process').fork;
......
cluster.fork = function(env) {
cluster.setupMaster();
const id = ++ids;
const workerProcess = createWorkerProcess(id, env);
const worker = new Worker({
id: id,
process: workerProcess
});
......
function createWorkerProcess(id, env) {
......
return fork(cluster.settings.exec, cluster.settings.args, {
env: workerEnv,
silent: cluster.settings.silent,
execArgv: execArgv,
gid: cluster.settings.gid,
uid: cluster.settings.uid
});
}