在开发的过程中,通过node app.js 运行服务器可。但它不适合在产环境下使用,因为到目前为这个服务 器还有几个重大问题
##存在问题
###不支持故障恢复
不知你是在调试的过程中注意,程序有错误发生时,整个进程就会结束,需要重新在终端中动服务器。这一点在开发中无可厚非,但在产环境下就是重的问题 了,因为一用访问时触发了程序中个的bug个服务器就了,所以我们在部署nodejs的时候一定要注意故障恢复
###没有日志
没有错误日志和访问日志
###无法利用多核
Nodejs是单线程的,一个进程只能使用一个CPU核心
###独占端口
假如个服务器只有一个网站,或者可以给每个网站分配一个的IP地址,不会有 端口冲突的问题。而很多时为了充分利用服务器的资源,我们会在同一个服务器上建多个网站,而且这些网站可能有的是PHP,有的是Rails,有的是Node.js。不能每个进程都80端口,所以我们有要通过代理来实现基于域名的端口共享。
###需要手动启动
先前每次动服务器都是通过在行中接入来实现的,但在产环境中, 特别是在服务器重启以后,全部手动是不现实的。因此,我们应该制作一个自动启动服务器的脚本,并且通过该脚本可以实现服务器等功能。
##解决问题
###日志功能
Express有两种模式:调试模式、产品模式,产品模式易于部署,设置模式方法:在MAC命令行
export NODE_ENV=production
我们实现访问日志和错误功能。访问日志就是录用对服务器的每个请求,包括端IP地址,访问时间,访问路径,服务器应以及端代理字符。而错误日志则录程序发生错误时的,由于调试中需要时看错误,将所有错误接显 示到终端可,而在产模式中,需要写入错误文件。
Express 提供了一个访问中间件,只需指定stream 参数为一个输出流可将访问日志写入文件。打开app.js,在最上方加入以下代码:
var fs = require('fs');
var accessLogfile = fs.createWriteStream('access.log',{flags:'a'});
var errorLogfile = fs.createWriteStream('error.log',{flags:'a'});
然后在app.configure第一行加入
app.use(express.logger({stream:accessLogfile}));
至于错误,需要实现错误,修改如下:
app.configure('production', function(){
app.use(function(err,req,res,next){
var meta = '[' + new Date() +']' +req.url + '\n';
errorLogfile.write(meta + err.stack + '\n');
next();
});
// app.use(express.errorHandler());
});
为了产生一个错误,我们改routes/index.js 中 / 的应函数,加入以下代码:
throw new Error('An error for test purposes.');
完整的app.js代码如下
/**
- Module dependencies.
*/
var express = require('express')
, routes = require('./routes')
, http = require('http')
, path = require('path')
, markdown=require('markdown-js')
, fs = require('fs');
//写入日志
var accessLogfile = fs.createWriteStream('access.log',{flags:'a'});
var errorLogfile = fs.createWriteStream('error.log',{flags:'a'});
var app = express();
var MongoStore = require('connect-mongo')(express);
var settings = require('./settings');
// Configuration
app.configure(function(){
app.set('port', process.env.PORT || 3300);
app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');
app.use(express.favicon(__dirname+'/public/logo.ico'));
// app.use(express.logger('dev'));
app.use(express.logger({stream:accessLogfile}));
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(express.cookieParser());
app.use(express.session({
secret: settings.cookieSecret,
store: new MongoStore({
db: settings.db
})
}));
app.use(app.router);
app.use(express.static(path.join(__dirname, 'public')));
});
app.configure('development', function(){
app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
});
app.configure('production', function(){
app.use(function(err,req,res,next){
var meta = '[' + new Date() +']' +req.url + '\n';
errorLogfile.write(meta + err.stack + '\n');
next();
});
// app.use(express.errorHandler());
});
app.engine('md', function(path, options, fn){
fs.readFile(path, 'utf8', function(err, str){
if (err) return fn(err);
str = markdown.parse(str).toString();
fn(null, str);
});
});
// Routes
app.get('/', routes.index);
app.get('/home', routes.home);
app.get('/about', routes.about);
app.get('/report', routes.report);
app.get('/submit', routes.getsubmit);
app.post('/submit', routes.postsubmit);
app.get('/login', routes.getlogin);
app.post('/login', routes.postlogin);
app.get('/logout', routes.logout);
app.post('/modify', routes.modify);
app.get('/login', routes.getlogin);
app.get('/doc/:author/:title', routes.getdoc);
app.get('/doc', routes.getdocindex);
app.get('/xxx', function(req, res) {
console.log('404 handler..')
res.render('404', {
status: 404,
title: 'NodeBlog',
user: req.session.user
});
});
http.createServer(app).listen(app.get('port'), function(){
console.log("Express server listening on port " + app.get('port'));
});
##多核多线程使用(cluster使用)
从0.6 版本开始,Node.js 提供了一个核心模块:cluster。cluster的功能是生成与当前进程相同的进程,并且允许进程和进程之间共享端口。Node.js另的一个核心模块 child_process 也提供了的进程生成功能,但最大的区别在于cluster允许跨进程端口复用,给我们的网络服务器开发带来了很大的方便
为了在外部模块调用app.js首先需要禁止服务器自启动。修改app.js,在app.listen (3000); 前后加上判断语:
if(!module.parent){
http.createServer(app).listen(app.get('port'), function(){
console.log("Express server listening on port " + app.get('port'));
});
}
这个语的功能是前模块是不是由其他模块调用的,如果不是,说它是接直接启动的,此时启动调试服务器。如果是,则不自动动服务器。以后直接调用node app.js服务器会接运行,但在其他模块中调用require(‘./app’) 则不会自动启动,需要显式地调用listen() 函数。
接下来就让我们通过cluster 调用app.js。创建cluster.js内容如下所示:
var cluster = require('cluster');
var os = require('os');
// 获取cpu数量
var numCPUs = os.cpus().length;
var workers = {};
if(cluster.isMaster){
// 主线程分支
cluster.on('death',function (worker) {
// 当一个工作进程结束时,重启工作进程
delete workers[worker.pid];
worker = cluster.fork();
workers[worker.pid] = worker;
});
// 初始开启与cpu数量相同的工作进程
for (var i = 0; i < numCPUs; i++) {
var worker = cluster.fork();
workers[worker.pid] = worker;
}
} else {
// 工作进程分支,启动服务器
var app = require('./app');
var http = require('http');
http.createServer(app).listen(app.get('port'), function(){
console.log("Express server listening on port " + app.get('port'));
});
}
// 当主进程被终止,关闭所有工作进程
process.on('SIGTERM',function () {
for(var pid in workers){
process.kill(pid);
}
process.exit(0);
});
对应修改app.js
var app = module.exports = express();
if(!module.parent){
http.createServer(app).listen(app.get('port'), function(){
console.log("Express server listening on port " + app.get('port'));
});
}
cluster.js 的功能是创建与CPU 核心个数同的服务器进程,以分利用多核CPU 的 资源。主进程生成个工作进程,并听工作进程结事件,工作进程结时,重新启动一个工作进程。分支进程产生时会自顶下重新行前程序,并通过分支进入工作进程分支,在其中读取模块并动服务器。通过cluster启动的工作进程可以接实现端口复用,因此所有工作进程只需听同一端口。主进程终时,还要主动关闭所有工作进程。
##启动脚本
接下来,我们还需要一个脚本来简化维工作。如果你维过Linux 服务器,会对 /etc/init.d/ 下面的脚本有印象。例如使用/etc/init.d/nginx start 和/etc/init.d/ nginx stop 可以动和关闭Nginx 服务器。我们通过bash也来实现一个的功能, 创建microblog 并使用chmod +x microblog 其执行权限脚本内容为:
#! /bin/sh
NODE_ENV=production
DAEMON="node cluster.js"
NAME=HINOCLab
DESC=HINOCLab
PIDFILE="HINOCLab.pid"
case "$1" in
start)
echo "starting $DESC: "
nohup $DAEMON > /dev/null &
echo $! > $PIDFILE
echo "$NAME."
;;
stop)
echo "stoping $DESC: "
pid='cat $PIDFILE'
kill $pid
rm $PIDFILE
echo "$NAME."
;;
esac
exit 0
注意不能有多余的空格
##共享80端口
虚拟主机就是让多个网站共享使用同一服务器,同一IP地址通过域的不同来分请求。主流的HTTP服务器都提供了主机支持,如Nginx、Apache、IIS等。以Nginx为例,介绍如何通过代理实现Node.js 主机。
在Nginx中设置代理和主机非常简单,下面是配置文件的一个示例:
server {
listen 80;
server_name mysite.com;
location / {
proxy_pass http://localhost:3000;
}
}
这个配置文件的功能是听访问mysite.com 80 端口的请求,并将所有的请求发给 http://localhost:3000, 到的Node.js 服务器。现在访问http://mysite.com/,就于服务器访问 http://localhost:3000 了。
在加了主机以后,还可以在Nginx配置文件中加访问文件的规则(具体请
参考Nginx文档),去app.js中的app.use(express.static(__dirname + ‘/public’));。 这可以接让Nginx 来处理文件,少代理以及Node.js 的开销。