执行环境
-
Express支持执行环境的概念,它是一种在生产、开发或测试模式中运行应用程序的方法。实际上你可以按自己的想法创建很多种不同的环境。
- 要记住,开发、生产和测试是“标准”环境,Express、Connect以及第三方中间件可能会基于这些环境做出决定。
- 换句话说,如果你有一个“临时”环境,则无法让它自动集成生产环境的属性。
-
尽管可以调用
app.set('env', 'production')
指定执行环境,但不建议这样做;- 因为那意味着不管什么情况,你的应用程序都会一直运行在那个环境中。
- 更糟的是,它可能在一个环境中开始运行,然后切换到另一个环境。
- 用环境变量NODE_ENV指定执行环境更好
//通过调用`app.get('env')`让它报告一下它运行在哪种模式下:
http.createServer(app).listen(app.get('port'), function(){
console.log( 'Express started in ' + app.get('env') +
' mode on http://localhost:' + app.get('port') +
'; press Ctrl-C to terminate.' );
});
- 如果现在启动服务器,将会看到它运行在开发模式下,因为如果你没有指定,开发模式就是默认模式。
//试着把它放在生产模式下:
$ export NODE_ENV=production
$ node meadowlark.js
- 如果用的是Unix/BSD系统或Cygwin,这里有个方便的语法,仅为一次命令执行期间设定环境:
$ NODE_ENV=production node meadowlark.js
- 如果在生产模式下启动Express,你可能会注意到有些组件不适合在生产模式下使用的警告信息。
环境特定配置
- 只是改变执行环境起不到太大的作用,尽管Express在生产模式下会输出更多警告到控制台中(比如告诉你被废弃的模块将来会被移除)。
- 在生产模式下,视图缓存会默认启用;
- 执行环境大体是一个可以利用的工具,可以轻松地决定应用程序在不同的环境下应该做何表现。
- 尽量缩小开发、测试和生产环境之间的差异, 也就是说应该保守地使用这个功能。
- 有些差异是不可避免的,比如,如果你的程序是高度数据库驱动的,你可能不想在开发期间干扰生产数据库,并且这是环境特定配置的良好候选用途。
- 另外一个影响不大的领域是更加详细的日志。你想在开发时记录的很多东西都没必要在生产环境中记录。
日志
- 给程序添加一些日志。在开发环境中,会用
Morgan
npm install --save morgan
,它的输出是便于查看的彩色文本。 - 在生产环境中,我们用
express-logger
npm install --save express-logger
,它支持日志循环(每24小时复制一次,然后开始新的日志,防止日志文件无限制地增长)。
//给程序文件添加日志支持:
switch(app.get('env')){
case 'development':
// 紧凑的、彩色的开发日志
app.use(require('morgan')('dev'));
break;
case 'production':
// 模块'express-logger'支持按日志循环
app.use(require('express-logger')({
path: __dirname + '/log/requests.log'
}));
break;
}
如果想实际看看日志的循环功能,可以编辑node_modules/express-logger/logger.js,修改变量defaultInterval,比如从24小时改成10秒(记住,修改node_modules中的包只能是出于实验或学习目的)。
扩展网站
- 扩展通常意味着向上扩展或向外扩展。
- 向上扩展是指让服务器变得更强:更快的CPU,更好的架构,更多内核,更多内存,等等。
- 向外扩展只是意味着更多的服务器。
- 随着云计算的流行和虚拟化的普及,服务器和计算能力的相关性变得越来越小,并且对于网站的扩展需求而言,向外扩展是成本收益率更高的办法。
- 在用Node开发网站时,应该总是考虑向外扩展的可能性。
在搭建一个设计好要向外扩展的网站时,最重要的是持久化。
- 除非所有服务器都能访问到那个文件系统,否则不应该用本地文件系统做持久化。
- 不过只读数据是个例外,比如日志和备份。
- 比如,可以把表单提交的数据备份到本地普通文件中,以防数据库接连失效。一旦遇到数据库中断的情况,到每个服务器上收集文件虽然麻烦,但最起码不会造成破坏。
用应用集群扩展
-
Node本身支持应用集群,它是一种简单的、单服务器形式的向外扩展。
-
使用应用集群,可以为系统上的每个内核(CPU)创建一个独立的服务器(有更多的服务器而不是内核数不会提高程序的性能)。
-
应用集群好在两个地方:
- 第一,它有助于实现给定服务器性能的最大化(硬件或虚拟机);
- 第二,它是一种在并行条件下测试程序的低开销方式。
-
给网站添加集群支持。尽管在主程序文件中做这些工作的做法十分普遍,但我们准备创建第二个程序文件,用之前一直在用的非集群程序文件在集群中运行程序。为此我们必须先对meadowlark.js做些轻微的调整:
function startServer() {
app.listen(process.env.PORT || 3000, function(){
console.log( 'Express started in ' + app.get('env') +
' mode on http://localhost:' + app.get('port') +
'; press Ctrl-C to terminate.' );
});
};
if(require.main === module){
// 应用程序直接运行;启动应用服务器
startServer();
} else {
// 应用程序作为一个模块通过"require"引入: 导出函数
// 创建服务器
module.exports = startServer;
}
//这样修改之后,meadowlark.js既可以直接运行(node meadowlark.js),也可以通过require语句作为一个模块引入。
- 然后我们会创建一个新脚本,meadowlark_cluster.js:
var cluster = require('cluster');
function startWorker() {
var worker = cluster.fork();
console.log('CLUSTER: Worker %d started', worker.id);
}
if(cluster.isMaster){
require('os').cpus().forEach(function(){
startWorker();
});
// 记录所有断开的工作线程。如果工作线程断开了,它应该退出,
// 因此我们可以等待exit事件然后繁衍 一个新工作线程来代替它
cluster.on('disconnect', function(worker){
console.log('CLUSTER: Worker %d disconnected from the cluster.',
worker.id);
});
// 当有工作线程死掉(退出)时,创建一个工作线程代替它
cluster.on('exit', function(worker, code, signal){
console.log('CLUSTER: Worker %d died with exit code %d (%s)',
worker.id, code, signal);
startWorker();
});
} else {
// 在这个工作线程上启动我们的应用服务器,参见meadowlark.js
require('./meadowlark.js')();
}
- 在这个JavaScript执行时,它或者在主线程的上下文中(当用node meadowlark_cluster.js直接运行它时),或者在工作线程的上下文中(在Node集群系统执行它时)。
- 属性cluster.isMaster和cluster.isWorker决定了运行在哪个上下文中。
- 在我们运行这个脚本时,它是在主线程模式下执行的,并且我们用cluster.fork为系统中的每个CPU启动了一个工作线程。
- 我们还监听了工作线程的exit事件,重新繁衍死掉的工作线程。最后,我们在else从句中处理工作线程的情况。既然我们将meadowlark.js配置为模块使用,只需要引入并立即调用它(记住,我们将它作为一个函数输出并启动服务器)。
- 如果你用的是虚拟机(比如Oracle的VirtualBox),则必须将VM配置为多个CPU。虚拟机一般默认只有一个CPU。
假定你在多核系统上,应该能看到一些工作线程启动了。如果你想看到不同工作线程处理不同请求的证据,在路由前添加下面这个中间件:
app.use(function(req,res,next){
var cluster = require('cluster');
if(cluster.isWorker) console.log('Worker %d received request',
cluster.worker.id);
});