nodejs
npm install (the command) to work behind proxy
config set strict-ssl false npm config set registry "http://registry.npmjs.org/" npm --proxy http://proxy_host:port install packagename
下载最新稳定版本 参考joyent installation nodejs开发指南express3源码
---------------------------------
安装依赖库
Python 版本 2.5 以上
sudo apt-get install g++ curl libssl-dev apache2-utils
sudo apt-get install git-core
---------------------------------
源码编译安装
tar -zxf node-v0.8.16.tar.gz
cd node-v0.8.16
./configure --prefix=/opt/node
make
sudo make install
#环境变量设置
export PATH=$PATH:$NODE_HOME/bin:$NODE_HOME/lib/node_modules
#终端中启用配置
source /opt/profile
---------------------------------
Binary安装
cd /opt/node
tar -zxf node-v0.8.16-linux-x64.tar.gz
#环境变量设置
export PATH=$PATH:$NODE_HOME/bin:$NODE_HOME/lib/node_modules
#终端中启用配置
source /opt/profile
---------------------------------
安装NPM(最近nodejs已集成npm)
集成npm安装
./configure --prefix=/opt/node/npm
make
sudo make install
#环境变量设置
export PATH=$PATH:$NPM_HOME/bin
#终端中启用配置
source /opt/profile
独立安装
curl http://npmjs.org/install.sh | sh
or
cd /opt/node/lib/node_modules/npm/scripts
sh install.sh
---------------------------------
命令行调试:
node debug *.js
远程调试:
node --debug[=port] script.js #脚本会正常执行不会暂停,默认情况下调试端口是 5858
node --debug-brk[=port] script.js #调试服务器在启动后会立刻暂停执行脚本,等待调试客户端连接
#在一个终端中
node --debug-brk debug.js
debugger listening on port 5858
#在另一个终端中
node debug 127.0.0.1:5858
---------------------------------
run 执行脚本,在第一行暂停
restart 重新执行脚本
cont, c 继续执行,直到遇到下一个断点
next, n 单步执行
step, s 单步执行并进入函数
out, o 从函数中步出
setBreakpoint(), sb() 在当前行设置断点
setBreakpoint(‘f()’), sb(...) 在函数f的第一行设置断点
setBreakpoint(‘script.js’, 20), sb(...) 在 script.js 的第20行设置断点
clearBreakpoint, cb(...) 清除所有断点
backtrace, bt 显示当前的调用栈
list(5) 显示当前执行到的前后5行代码
watch(expr) 把表达式 expr 加入监视列表
unwatch(expr) 把表达式 expr 从监视列表移除
watchers 显示监视列表中所有的表达式和值
repl 在当前上下文打开即时求值环境
kill 终止当前执行的脚本
scripts 显示当前已加载的所有脚本
version 显示 V8 的版本
---------------------------------
node-inspector是一个完全基于Node.js的开源在线调试工具
#node-inspector使用了WebKit Web Inspector,因此只能在Chrome等WebKit内核的浏览器中使用
命令安装:npm install -g node-inspector
在终端中通过node --debug-brk=5858 debug.js命令连接你要除错的脚本的调试服务器
启动 node-inspector:node-inspector
在浏览器中打开http://127.0.0.1:8080/debug?port=5858,即可显示Web调试工具
---------------------------------
process用于描述当前Node.js进程状态的对象,提供了一个与操作系统的简单接口
argv.js:
console.log(process.argv);
$ node argv.js 2012 name=Eric --v "ningbo"
[ 'node',
'/home/work/argv.js',
'2012',
'name=Eric',
'--v',
'ningbo' ]
---------------------------------
#http://nodejs.org/api/process.html
process.nextTick(callback)的功能是为事件循环设置一项任务,Node.js会在下次事件循环调响应时调用callback
compute()和somethingComplicated()是两个较为耗时的函数:
function doSomething(args, callback) {
somethingComplicated(args);
callback();
}
doSomething(function onEnd() {
compute();
});
---------------------------------------
function doSomething(args, callback) {
somethingComplicated(args);
process.nextTick(callback);
}
doSomething(function onEnd() {
compute();
});
#不要使用 setTimeout(fn,0)代替 process.nextTick(callback),前者比后者效率要低得多
---------------------------------
util.inherits(constructor, superConstructor)是一个实现对象间原型继承的函数
---------------------------------
#http://nodejs.org/api/util.html
#showHidden-隐藏信息 depth-最大递归的层数,null无限递归 colors-ANSI颜色编码
util.inspect(object,[showHidden],[depth],[colors])是一个将任意对象转换为字符串的方法,通常用于调试和错误输出
---------------------------------
事件发射器
events.EventEmitter是事件发射与事件监听器功能的封装
var events = require('events');
var emitter = new events.EventEmitter();
emitter.on('someEvent', function(arg1, arg2) {
console.log('listener1', arg1, arg2);
});
emitter.on('someEvent', function(arg1, arg2) {
console.log('listener2', arg1, arg2);
});
#当error发生时,EventEmitter规定如果没有响应的监听器,Node.js会把它当作异常,退出程序并打印调用栈
#一般要为发射error事件的对象设置监听器,避免遇到错误后整个程序崩溃
emitter.on('error', function() {
console.log('error');
});
emitter.emit('someEvent', 'Eric', 2012);
---------------------------------
文件系统-fs
---------------------------------
HTTP-http
---------------------------------
控制权转移:
app.all('/user/:username', function(req, res) {
res.send('all methods captured');
});
app.get('/user/:username', function(req, res) {
res.send('user: ' + req.params.username);
});
#请求总是被前一条路由规则捕获,后面的规则会被忽略
app.all('/user/:username', function(req, res, next) {
console.log('all methods captured');
next();#路由控制权转移给后面的规则
});
app.get('/user/:username', function(req, res) {
res.send('user: ' + req.params.username);
});
-----------提高代码的复用程度-----------
var users = {
'person': {
name: 'Eric',
website: 'http://wen12128.cnblogs.com'
}
};
app.all('/user/:username', function(req, res, next) {
// 检查用户是否存在
if (users[req.params.username]) {
next();
} else {
next(new Error(req.params.username + ' does not exist.'));
}
});
app.get('/user/:username', function(req, res) {
// 用户一定存在,直接展示
res.send(JSON.stringify(users[req.params.username]));
});
app.put('/user/:username', function(req, res) {
// 修改用户信息
res.send('Done');
});
---------------------------------
#更改默认资源库:
npm config set registry "http://npm.hacknodejs.com/"
#NPM资源库镜像:{http://registry.npmjs.vitecho.com}-{http://npm.hacknodejs.com/}
npm --registry "http://npm.hacknodejs.com/" install underscore
---------------------------------
安装supervisor:
#监视你对代码的改动,并自动重启 Node.js
npm install -g supervisor
---------------------------------
安装express:
#-d代表把相依性套件也一起安装
查看安装包:npm ls
npm install -gd express
express --help
express -t ejs demo
cd demo && npm install
REST风格的路由规则:
GET:[获取app.get(path, callback)]请求获取指定资源。
HEAD:请求指定资源的响应头。
POST:[新增app.post(path, callback)]向指定资源提交数据。
PUT:[更新app.put(path, callback)]请求服务器存储一个资源。
DELETE:[删除app.delete(path, callback)]请求服务器删除指定资源。
PATCH:[app.patch(path, callback)]IETF RFC 5789新增的HTTP方法,功能定义是部分更新某个资源
TRACE:[app.trace(path, callback)]回显服务器收到的请求,主要用于测试或诊断。
CONNECT:[app.connect(path, callback)]HTTP/1.1 协议中预留给能够将连接改为管道方式的代理服务器。
OPTIONS:[app.options(path, callback)]返回服务器支持的HTTP请求方法
所有方法:[app.all(path, callback)]
#幂等-重复请求多次与一次请求的效果是一样
---------------------------------
启用页面布局功能:
var partials = require('express-partials')
app.configure中添加以下内容:
app.use(partials());
启用其它布局模板:
#页面模板时套用admin.ejs作为页面布局
function(req, res){res.render('userlist',{title: '用户列表后台管理系统',layout: 'admin'});};
---------------------------------
片段视图:
partials:重复的内容,用于迭代显示,将相对独立的页面块分割出去,而且可以避免显式地使用循环迭代
app.get('/list',function(req,res){res.render('list',{title:'List',items:[2012,'Eric','express','Node.js']});});
list.ejs:<ul><%- partial('listitem', items) %></ul>
listitem.ejs:<li><%= listitem %></li>
---------------------------------
视图助手:
#express3.0中locals代替dynamicHelpers():
app.use(function(req,res,next){
var err = req.flash('error'),success = req.flash('success');
res.locals.user = req.session.user;
res.locals.error = err.length ? err : null;
res.locals.success = success.length ? success : null;
next();
});
允许在视图中访问一个全局的函数或对象,不用每次调用视图解析的时候单独传入
静态视图助手:任何类型的对象,包括接受任意参数的函数,但访问到的对象必须与用户请求无关,通过app.helpers()函数注册
动态视图助手:只能是一个函数,该函数不能接受参数,但可以访问req和res对象,通过app.dynamicHelpers()注册
var util = require('util');
app.helpers({
inspect: function(obj) {
return util.inspect(obj, true);
}
});
app.dynamicHelpers({
headers: function(req, res) {
return req.headers;
}
});
app.get('/helper', function(req, res) {
res.render('helper', {
title: 'Helpers'
});
});
#视图中调用:
<%=inspect(headers)%>
---------------------------------
安装MongoDB:
工程目录中package.json->dependencies中添加一行代码:
,"mongodb": "*" #*会默认获取最新版本
然后运行cd microblog && npm install更新依赖的模块,接下来在工程的目录中创建settings.js文件用于保存数据库的连接信息:
module.exports = {
cookieSecret: 'microblog',
db: 'microblog',
host: 'localhost',
};
在工程目录下新建models目录并创建db.js:
var settings = require('../settings');
var Db = require('mongodb').Db;
var Connection = require('mongodb').Connection;
var Server = require('mongodb').Server;
module.exports = new Db(settings.db, new Server(settings.host, Connection.DEFAULT_PORT, {}));
---------------------------------
会话支持:
工程目录中package.json->dependencies中添加一行代码:
"connect-mongo": ">= 0.1.7"
然后运行cd microblog && npm install更新依赖的模块,接下来在app.js中添加:
var MongoStore = require('connect-mongo');
var settings = require('./settings');
app.configure(function(){
app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');
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(__dirname + '/public'));
});
---------------------------------
模块的类型:
Node.js的模块可以分为两大类,一类是核心模块,另一类是文件模块
核心模块拥有最高的加载优先级,换言之如果有模块与其命名冲突,Node.js总是会加载核心模块
在不显式指定文件模块扩展名的时候,Node.js会分别试图加上.js(javascript) > .json(json) > .node(c/c++)扩展名
核心模块:
Node.js标准API中提供的模块,如fs、http、net、vm等
---------------------------------
按路径加载模块:
相对路径:require参数以“./”或“../”开头
绝对路径:require('/home/ms') 将会按照优先级依次尝试加载/home/ms.js、/home/ms.json、/home/ms.node
在node_modules中加载模块:
如果require参数不以“/”、“./”或“../”开头,而该模块又不是核心模块,那么就要通过查找node_modules加载模块
如果没有找到,则会在当前目录的上一层中的node_modules目录中继续查找,反复执行这一过程,直到遇到根目录为止
require('ms.js'):/home/work/node_modules/ms.js > /home/node_modules/ms.js > /node_modules/ms.js
---------------------------------
加载缓存:
Node.js模块不会被重复加载,这是因为Node.js是通过实际文件名缓存所有加载过的文件模块,而不是通过require()提供的参数缓存的
---------------------------------
加载顺序:
require(some_module)的加载顺序:
如果some_module是一个核心模块,直接加载,结束
如果some_module以“/”、“./”或“../”开头,按路径加载some_module,结束。
假设当前目录为current_dir,按路径加载current_dir/node_modules/some_module
如果加载成功,结束
如果加载失败,令current_dir为其父目录
重复这一过程,直到遇到根目录,抛出异常,结束
---------------------------------
循环的陷阱:闭包
---------------------------------
深层的回调函数嵌套:改变设计模式,时刻注意降低逻辑之间的耦合关系
---------------------------------
应用部署:
日志功能:
Express支持两种运行模式:开发模式和产品模式,前者的目的是利于调试,后者则是利于部署
使用产品模式运行服务器的方式很简单,命令行输入:NODE_ENV=production node app.js
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();
});
});
---------------------------------
多核提高性能及故障恢复:
Node.js的核心模块child_process:生成与当前进程相同的子进程
cluster的功能是生成与当前进程相同的子进程,并且允许父进程和子进程之间共享端口
区别在于cluster允许跨进程端口复用
为了外部模块能调用app.js,需要禁止服务器自动启动。修改app.js:
var app = express() --> var app = module.exports = express()
if (!module.parent) {//判断当前模块是不是由其他模块调用的
http.createServer(app).listen(3000, function(){
console.log("Express server listening on port %d in %s mode", 3000, app.settings.env);
});
}
cluster.js的功能是创建与CPU核心个数相同的服务器进程,以确保充分利用多核CPU的资源。通过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');
app.listen(3000);
}
// 当主进程终止时,关闭所有工作进程
process.on('SIGTERM', function () {
for (var pid in workers) {
process.kill(pid);
}
process.exit(0);
});
---------------------------------
启动脚本:通过nohup启动服务器,使进程不会因为退出终端而关闭
#! /bin/sh
NODE_ENV=production
DAEMON="node cluster.js"
NAME=Microblog
DESC=Microblog
PIDFILE="/usr/works/microblog.pid"
case "$1" in
start)
echo "Starting $DESC: "
nohup $DAEMON > /usr/works/microblog &
echo $! > $PIDFILE
echo "$NAME."
;;
stop)
echo "Stopping $DESC: "
pid=`cat $PIDFILE`
kill $pid
rm $PIDFILE
echo "$NAME."
;;
esac
exit 0
---------------------------------
反向代理:
Nginx:
server {
listen 80;
server_name microblog.com;
location / {
proxy_pass http://localhost:3000;
}
}
---------------------------------
Nginx配置文件中添加访问静态文件的规则
删除app.js中的app.use(express.static(path.join(__dirname, 'public')));