Node.JS
集群clusterfork运行Node.js
使用Node.js最大的原因是因为简化,通过使用单线程范式,从而避免了多线程环境中复杂的线程编程,比如资源竞争和死锁。当然,以单线程方式运行一个Web服务器的缺点是只能利用单核CPU,而现在基本进入了多核时代,这就对资源造成了浪费,而通过Node.js的集群Cluster方式很容易更好地利用资源。
当你以集群方式运行Node.js时,将会跨多个处理器,这会更加稳定,因为在单流程情况下,当你的应用遭遇错误时,只能重新启动流程,在这段重启时间内,你的服务就中断了,集群方式可以保持一个主流程时刻了解它的子流程是否失败,会在其失败情况下自动将其请求转发到其他子流程,一直到失败的子流程重新启动。
加入集群非常容易,假设我们下面一段代码:
var express = require('express');
var app = express();
app.get('/', function(req, res) {
res.send('Hello World!');
});
var server = app.listen(3000, function() {
console.log('Server started on port 3000');
});
下面命令是是安装启动:
npm install express
node server
在浏览器打开http://localhost:3000会得到hello world。
下面是加入集群方式的代码:
var cluster = require('cluster');
if (cluster.isMaster) {
var numCPUs = require('os').cpus().length;
for (var i = 0; i < numCPUs; i++) {
cluster.fork();
}
} else {
var express = require('express');
var app = express();
app.get('/', function(req, res) {
res.send('Hello World!');
});
var server = app.listen(3000, function() {
console.log('Server started on port 3000');
});
}
这段代码主要增加了cluster,当应用启动后,将自己作为主流程,主流程根据CPU个数创建一个或多个子流程,一般情况下,子流程个数等于CPU的核数,主流程自己不是一个服务器,它只是负责创建和维护子流程,因为如果主流程也对外服务,一旦其失败,所有子流程都会失败。
重启
如果一个子流程失败了,主流程能够重启启动它,代码如下:
var cluster = require('cluster');
if (cluster.isMaster) {
var numCPUs = require('os').cpus().length;
for (var i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on('exit', function() {
console.log('A worker process died, restarting...');
cluster.fork();
});
} else {
var express = require('express');
var app = express();
app.get('/', function(req, res) {
res.send('Hello World!');
});
app.get('/explode', function(req, res) {
setTimeout(function() {
res.send(this.wont.go.over.well);
}, 1);
});
var server = app.listen(3000, function() {
console.log('Server started on port 3000');
});
}
当你访问http://localhost:3000/explode 时,你会引起一个子流程错误,会看到控制端的堆栈跟踪,但是你的应用还是在运行,但是如果你加载explode页面多次,你会杀死所有的子流程,应用就会退出。那么有灰色背景的一段代码就能够防止在子流程退出时重新启动。
性能测试
当我们使用8核集群方式进行性能测试,单流程结果如下:
Transactions: 15932 hits
Availability: 100.00 %
Elapsed time: 19.41 secs
Data transferred: 0.18 MB
Response time: 0.02 secs
Transaction rate: 820.64 trans/sec
Throughput: 0.01 MB/sec
Concurrency: 14.79
Successful transactions: 15932
Failed transactions: 0
Longest transaction: 0.03
Shortest transaction: 0.01
而启动了集群方式的8核性能如下:
Transactions: 34479 hits
Availability: 100.00 %
Elapsed time: 19.38 secs
Data transferred: 0.39 MB
Response time: 0.00 secs
Transaction rate: 1779.38 trans/sec
Throughput: 0.02 MB/sec
Concurrency: 7.93
Successful transactions: 34489
Failed transactions: 0
Longest transaction: 0.07
Shortest transaction: 0.00
注意到第一行数据对比,有116%的性能提高。当然Node.js不是CPU-bound重CPU计算的理想选择,真实世界的结果可能与这个测试有差距,如果你有依赖CPU计算的工作,可使用Node.js作为代理,委托给其他物理机器去执行。