第1页
软件开发实践分享
@范圣刚 - 2013.12.05
第2页
主要内容
模块 异常捕捉和错误处理
同步和异步 运⾏行与⽇日志 ⼏几个 Tips
第3页
模块(Modules)
第4页
模块组织
第5页
模块系统
• 使⽤用 require ⽅方法,使⽤用模块标识,返回导出的 API • 模块名称是字符串,可以包含路径 • 要从模块中导出的必须要明确指定
CommonJS 模块系统要求在 Node 中的实现
第6页
⽂文件和模块⼀一对⼀一
• 传⼊入模块标识字符串
• var http = require(‘http’)
• 也可以指定其中的⼀一种对象,⽽而不是所有对象
• var spawn = require(‘child_process’).spawn
• Node 本⾝身的模块,或是在 node_modules 下⾯面 的模块,可以直接使⽤用 module 的 identifier
• 否则需要使⽤用 / 指定路径
• require(‘./mymodule’); 或者全路径 • require(‘/path/to/mymodule’);
• 扩展名可以是 .js, .node, .json
第7页
模块加载过程
http://nodejs.org/api/modules.html
第8页
模块循环引用的问题
http://nodejs.org/api/modules.html
第9页
模块的导出:exports
第10页
单次加载的问题
第11页
导出对象的封装
var Hello = require(‘./singleobject’).Hello
第12页
模块的导出:module.exports
第13页
作为 class 导出
第14页
模块复用:node_modules
•package.json •git •npm init •npm adduser •npm publish •本地测试:npm install . -g
测试和发布⾃自⼰己的 Module
第15页
同步和异步
第16页
使用 Underscore.js
简化同步操作
第17页
异步编程的常见陷阱
第18页
异步操作的工作流
同步遍历⽂文件并读取内容的代码
第19页
改成异步后的代码
第20页
Async 的工作流控制和任务组织
简化异步数据集和函数集操作的利器
第21页
Async:简化数据集操作
第22页
Async:简化任务集操作
series & parallel
第23页
Async:waterfall
第24页
Async:queue
第25页
async.waterfall([
function (cb) {
engine.speak(option, cb);
}
, function (voice, cb) {
var mp3file = voice_temp_dir + voice_prefix + entry._id + '.mp3';
async.parallel([
function (cb) {
fs.writeFile(mp3file, voice, {encoding: 'base64'}, cb);
}
, function (cb) {
TranslationMemory.findByIdAndUpdate(entry._id, {'mp3voice':
voice.toString('base64'), 'datetime': new Date}, cb);
}
]
, function (err, results) {
return cb(err, results);
}
);
}
]
, function (err, result) {
return callback(err, result);
}
);
Async ⽰示例:waterfall + parallel
第26页
var updateAudio = function (limit, callback) { async.waterfall([ function(cb) { TranslationMemory.find({mp3voice: {$exists: false}}, null, {limit: 1000}, cb); } , function(entries, cb) { async.eachLimit(entries, limit, speakAndUpdate, cb); } ] , function(err, result) { return callback(null, result); } ); };
Async ⽰示例:waterfall + eachLimit
第27页
异常捕捉和错误处理
第28页
assert
第29页
顺序执行和异常捕捉
第30页
JSON 解析的异常捕捉
第31页
//Define divider as a syncrhonous function var divideSync = function(x,y) {
// if error condition? if ( y === 0 ) {
// "throw" the error safely by returning it return new Error("Can't divide by zero"); } else { // no error occured, continue on return x/y; } };
!
// Divide 4/0 result = divideSync(4,0); // did an error occur? if ( result instanceof Error ) {
// handle the error safely console.log('4/0=err', result); } else { // no error occured, continue on console.log('4/0='+result); }
同步执⾏行返回错误对象
第32页
异步(嵌套)回调和异常处理
第33页
回调内抛出的异常
function async(callback) { process.nextTick(function(){ throw new Error("Something went wrong"); callback(); });
}
!
try { async(function(){ console.log("It worked!"); });
} catch(error) { console.log("This is never printed.");
}
第34页
未捕获异常的处理
process.on('uncaughtException', function(err) { // handle the error safely console.log(err);
});
!
// the asynchronous or synchronous code that emits the otherwise uncaught error var err = new Error('example'); throw err;
第35页
var divide = function(x,y,next) { // if error condition? if ( y === 0 ) { // "throw" the error safely by calling the completion callback // with the first argument being the error next(new Error("Can't divide by zero")); } else { // no error occured, continue on next(null, x/y); }
};
!
divide(4,0,function(err,result){ // did an error occur? if ( err ) { // handle the error safely console.log('4/0=err', err); } else { // no error occured, continue on console.log('4/0='+result); }
});
异步返回错误
第36页
//Definite our Divider Event Emitter var events = require('events'); var Divider = function(){
events.EventEmitter.call(this); }; require('util').inherits(Divider, events.EventEmitter); // Add the divide function Divider.prototype.divide = function(x,y){
// if error condition? if ( y === 0 ) {
// "throw" the error safely by emitting it var err = new Error("Can't divide by zero"); this.emit('error', err); } else { // no error occured, continue on this.emit('divided', x, y, x/y); } // Chain return this; };
!
// Create our divider and listen for errors var divider = new Divider(); divider.on('error', function(err){
// handle the error safely console.log(err); }); divider.on('divided', function(x,y,result){ console.log(x+'/'+y+'='+result); });
!
// Divide divider.divide(4,2).divide(4,0);
使⽤用 Event
第37页
运行和日志
第38页
Linux 上后台运行
第39页
以生产环境运行
NODE_ENV=production node yourscript.js
第40页
故障恢复和进程管理 - forever
https://github.com/nodejitsu/forever
第41页
log4js
https://github.com/nomiddlename/log4js-node
第42页
数据库:Mongoose V3
•从 Node.js MongoDB driver 迁移到 Mongoose
http://mongodb.github.io/node-mongodb-native/ http://mongoosejs.com
第43页
app.getOneDocument = function(collection_name, query, callback) { db.collection(collection_name).find(query).nextObject(callback); }
!
app.getAllDocuments = function(collection_name, query, callback) { db.collection(collection_name).find(query).toArray(callback); }
!
app.setOneDocument = function(collection_name, query, update, callback) { db.collection(collection_name).update(query, {$set: update}, callback); }
!
app.addOneDocument = function(collection_name, document, callback) { db.collection(collection_name).insert(document, {safe: true}, callback); }
store.addOneDocument('event_security', event_signin, function(err, event_result) { });
对 MongoDB node.js driver 的简单封装
第44页
'use strict';
!
var mongoose = require('mongoose') , Schema = mongoose.Schema , ObjectId = Schema.ObjectId;
!
var app = module.exports = {}; var ProductSchema = new Schema({ amount: Number , appname: String , category: String , course: { id: String , name: String
, level: Number , level_upgrade_after:Array
} , enabled: Boolean , datetime: { type: Date, default: Date.now } });
!
app.ProductModel = mongoose.model('products', ProductSchema, 'products');
每个 schema 映射到⼀一个 MongoDB 的 collection
第45页
Mongoose Features
• Schemas •schema 可以具有自定义的实例和静态方法,以及get/set •Virtuals - 方便使用又不持久化到 MongoDB
• Models •创建:new … save() / create() •find, findById, findOne, findOneAndUpdate, remove … •validation
• Middleware •init, validate, save, remove(触发器) •pre & post
•其他:连接池,跨表查询,plugins等
第46页
总结
•操作系统:Ubuntu 12.04 64位 •托管:Linode 2GB * 3, 1-dev,2-production •版本:使用 Git 进行版本控制和部署(gitlab) •Web Server:Nginx 1.4.3(服务静态文件和代理) •DataBase:MongoDB + Mongoose •Load Balancer:Linode NodeBalancers •进程管理:forever + init.d(service) •日志:log4js + logrotate.d(转储,压缩,每天) •同步和异步JS:Underscore + Async
第47页
开发小故事
•调试:node-inspector(webkit+远程调试) •‘use strict’:全局变量; •npm install <package_name> —save : 自动更
新 package.json
•Canvas -> PhantomJS •消息队列:kue -> RabbitMQ
第48页
我为什么使用 Node.js ?
我的开发⼯工具箱 Web 应⽤用 Apps 开发
⽹网络和系统编程 脚本和⼩小⼯工具
数据库 数据交换
现在
Node.js + Express + Backbone
Sencha Touch + PhoneGap
Node.js
以前
Ruby on Rails Java SSH iOS Android
C++
Node.js, Ruby MongoDB, redis
JSON
Ruby, C++
SQL Server, Oracle, MySQL
⼆二进制的 protocol,http urlencode
简单⾼高效
第49页
Node.js vs. Java (via PayPal)
•开发
•构建过程使用更少的开发者,速度反而快一倍 •书写的代码行数要少33% •文件个数少了 40%
•性能
•每秒处理的请求翻倍(Node.js 一核心,Java 五核 心)
•平均响应时间下降了 35%(快了200ms)
https://www.paypal-engineering.com/2013/11/22/node-js-at-paypal/
第50页
不明觉厉
•examples -> Mocha •express -> kraken •callback -> Promise + Q.js •libuv,v8, Linux C++
第51页
Thank you!
第52页
http://gitlab.org/
第53页
Performance
第54页
ab -n 10000 -c 1000
第55页
Node.js vs. Apache + PHP
1M20K HTTP REQUEST
Node.js
Apache + PHP
第56页
Node.js vs. Apache + Php
• Node.js is much faster than Apache+PHP • Many more requests per second • Higher transfer rate with much smaller number of
failed requests at the same time
第57页
Node.js vs. Go
第58页
Node.js vs. Go
• Go 的数据处理优于 Node.js(⽐比如运⾏行冒泡排 序)
• Node.js 的 HTTP 处理和伸缩性⽐比 Go 更有效率 • 团队经验(JavaScript Vs. ⼀一种全新的语⾔言)