昨天搞定了一个小网站的搭建,用了 node.js,另外为了能在一个 VPS 上搭建多个网站,用了 nginx 作为反向代理。
嗯,从维基上复制了一下~
node.js
Node.js是一个事件驱动I/O伺服端JavaScript环境,基于V8。目的是为了提供撰写可扩充网络程式,如web服务。第一个版本由Ryan Dahl于2009年释出,后来,Joyent雇用了Dahl,并协助发展Node.js。
nginx
nginx(发音同engine x)是一款由俄罗斯程序员Igor Sysoev所开发轻量级的网页服务器、反向代理服务器以及电子邮件(IMAP/POP3)代理服务器。
cluster
在 node.js 0.6.0 之前,有一个第三方的 node.js 模块 cluster,用来进行多核服务器上运行 node.js,以及提供扩展的支持。但是在 node.js 0.6.0 之后,node.js 本身就提供了 cluster 的支持,另外,第三方的 cluster 也与 node.js 0.6 有兼容性问题。目前 node.js 的稳定版本是 0.6.5,因此需要使用原生的 cluster 来代替第三方的 cluster。
幸好内置的 cluster 也足够简单,如果只是为了多核负载均衡,以及支持即时服务重启的话,只需要写一点的代码就可以完成这些功能了。
server.js
var path = require('path');
var http = require('http');
var cluster = require('cluster');
var NODE_ENV = process.env.NODE_ENV || 'production';
var appName = path.basename(__dirname);
var appPort = 9000;
var numCPUs = require('os').cpus().length;
if (cluster.isMaster) {
process.title = appName + ' master';
console.log(process.title, 'started');
// 根据 CPU 个数来启动相应数量的 worker
for (var i = 0; i < numCPUs; i++) {
cluster.fork();
}
process.on('SIGHUP', function() {
// master 进程忽略 SIGHUP 信号
});
cluster.on('death', function(worker) {
console.log(appName, 'worker', '#' + worker.pid, 'died');
cluster.fork();
});
} else {
process.title = appName + ' worker ' + process.env.NODE_WORKER_ID;
console.log(process.title, '#' + process.pid, 'started');
process.on('SIGHUP', function() {
// 接收到 SIGHUP 信号时,关闭 worker
process.exit(0);
});
http.Server(function(req, res) {
res.writeHead(200);
res.end('Worker ' + process.env.NODE_WORKER_ID);
}).listen(8000);
}
运行服务 node server.js
:
nodejs master started
nodejs worker 1 #38928 started
nodejs worker 3 #38930 started
nodejs worker 2 #38929 started
nodejs worker 4 #38931 started
如果直接 kill 掉某一个 worker,kill 38928
:
nodejs worker #38931 died
nodejs worker 5 #38934 started
可以看到一个新的 worker 会马上启动,这就保证了服务的不间断性。
通常情况下,我们不会在一个 IP 上只部署一个网站。在使用 node.js 时,可以使用 connect 提供的 vhost 支持 Virtual Host,但是,这也限制了服务器只能用 node.js,而不能同时使用其他的服务,例如再安装一个 PHP 服务之类。
这时就可以使用 nginx 的反向代理来解决了,用户在访问网站时,请求先到 nginx 进行处理,如果是 node.js 站点的话,将请求转发到 node.js 的服务,然后再将 node.js 服务的结果返回给用户。
在 nginx 中设置反向代理很简单,一句 proxy_pass
就可以搞定:
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://localhost:9000;
}
}
在添加了 Virtual Host 之后,就可以把一些静态资源,例如 CSS、JavaScript 之类的文件,直接交给 nginx 来处理,而不是什么请求都需要到 node.js 这一层去处理,也省去反向代理这一关的消耗。
使用 express 这个 node.js web framework 来创建网站时,可以配合 connect 这个中间件来实现 session 支持。
默认情况下,connect 的 session 是使用内置的内存存储来存放 session 信息,这时如果 node.js 服务一旦重启,所有的 session 信息都会丢失,这对于用户来说不是个好体验,那么我们可以用外部的存储来存放 session 信息,例如 redis。
要让 connect 使用 redis 作为 session 存储的话也是很方便的:
var express = require('express');
var RedisStore = require('connect-redis')(express);
var app = express.createServer(
express.session({ secret: 'keyboard cat', store : new RedisStore() })
);
在调试的时候,经常需要重新启动 node.js 以便修改过的文件生效,原来第三方的 cluster 有一个配置项,可以很方便的配置监测时间间隔,文件改动后自动重新启动 worker,但是原生的 cluster 就没有这个功能了,需要自己来实现。
fs 模块提供了 watch 函数,可以方便的监测文件修改,使用这个就可以来实现文件修改后自动重启 woker 功能了。
if (cluster.isMaster) {
process.title = appName + ' master';
console.log(process.title, 'started');
var workers = [];
// 根据 CPU 个数来启动相应数量的 worker
for (var i = 0; i < numCPUs; i++) {
var worker = cluster.fork();
workers.push(worker.pid);
}
process.on('SIGHUP', function() {
// master 进程忽略 SIGHUP 信号
});
// 监测文件改动,如果有修改,就将所有的 worker kill 掉
fs.watch(__dirname, function(event, filename) {
workers.forEach(function(pid) {
process.kill(pid);
});
});
cluster.on('death', function(worker) {
var index = workers.indexOf(worker.pid);
if (index != -1) {
workers.splice(index, 1);
}
console.log(appName, 'worker', '#' + worker.pid, 'died');
worker = cluster.fork();
workers.push(worker.pid);
});
}
这样,每次文件保存之后,node.js 都会自动重启,从而避免了每次保存文件要手动重启服务的麻烦。
当然,在使用监测文件自动重启的时候,最好加上 NODE_ENV 的判断,在 development 的时候才进行自动重启,而 production 的时候使用手动重启就够了。
总的来说,使用 node.js 来构建网站还是很方便的,加上 nginx 反向代理之后,与使用 PHP 之前也没有很大的区别,又可以享受到 node.js 的高效。
嗯,就这样了,希望此文对你有所帮助。
- http://zh.wikipedia.org/wiki/Node.js
- http://nodejs.org/docs/v0.6.5/api/cluster.html
- http://learnboost.github.com/cluster/
- http://expressjs.com/
- http://senchalabs.github.com/connect/
- http://redis.io/
–EOF–
from Xu Jiwei