软件开发实践分享 - 范圣刚的博客 · 我的开发具箱 现在 以前 web 应 node.js +...

58
软件开发实践分享 @范圣刚 - 2013.12.05

Upload: others

Post on 02-Oct-2020

20 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: 软件开发实践分享 - 范圣刚的博客 · 我的开发具箱 现在 以前 Web 应 Node.js + Express + Backbone Ruby on Rails Java SSH Apps 开发 Sencha Touch + PhoneGap iOS

软件开发实践分享

@范圣刚 - 2013.12.05

Page 2: 软件开发实践分享 - 范圣刚的博客 · 我的开发具箱 现在 以前 Web 应 Node.js + Express + Backbone Ruby on Rails Java SSH Apps 开发 Sencha Touch + PhoneGap iOS

主要内容

模块 异常捕捉和错误处理

同步和异步 运⾏行与⽇日志 ⼏几个 Tips

Page 3: 软件开发实践分享 - 范圣刚的博客 · 我的开发具箱 现在 以前 Web 应 Node.js + Express + Backbone Ruby on Rails Java SSH Apps 开发 Sencha Touch + PhoneGap iOS

模块(Modules)

Page 4: 软件开发实践分享 - 范圣刚的博客 · 我的开发具箱 现在 以前 Web 应 Node.js + Express + Backbone Ruby on Rails Java SSH Apps 开发 Sencha Touch + PhoneGap iOS

模块组织

Page 5: 软件开发实践分享 - 范圣刚的博客 · 我的开发具箱 现在 以前 Web 应 Node.js + Express + Backbone Ruby on Rails Java SSH Apps 开发 Sencha Touch + PhoneGap iOS

模块系统•使⽤用 require ⽅方法,使⽤用模块标识,返回导出的 API

•模块名称是字符串,可以包含路径

•要从模块中导出的必须要明确指定

CommonJS 模块系统要求在 Node 中的实现

Page 6: 软件开发实践分享 - 范圣刚的博客 · 我的开发具箱 现在 以前 Web 应 Node.js + Express + Backbone Ruby on Rails Java SSH Apps 开发 Sencha Touch + PhoneGap iOS

⽂文件和模块⼀一对⼀一•传⼊入模块标识字符串

• var http = require(‘http’)

•也可以指定其中的⼀一种对象,⽽而不是所有对象 • var spawn = require(‘child_process’).spawn

• Node 本⾝身的模块,或是在 node_modules 下⾯面的模块,可以直接使⽤用 module 的 identifier

•否则需要使⽤用 / 指定路径

• require(‘./mymodule’); 或者全路径

• require(‘/path/to/mymodule’);

•扩展名可以是 .js, .node, .json

Page 7: 软件开发实践分享 - 范圣刚的博客 · 我的开发具箱 现在 以前 Web 应 Node.js + Express + Backbone Ruby on Rails Java SSH Apps 开发 Sencha Touch + PhoneGap iOS

模块加载过程

http://nodejs.org/api/modules.html

Page 8: 软件开发实践分享 - 范圣刚的博客 · 我的开发具箱 现在 以前 Web 应 Node.js + Express + Backbone Ruby on Rails Java SSH Apps 开发 Sencha Touch + PhoneGap iOS

模块循环引用的问题

http://nodejs.org/api/modules.html

Page 9: 软件开发实践分享 - 范圣刚的博客 · 我的开发具箱 现在 以前 Web 应 Node.js + Express + Backbone Ruby on Rails Java SSH Apps 开发 Sencha Touch + PhoneGap iOS

模块的导出:exports

Page 10: 软件开发实践分享 - 范圣刚的博客 · 我的开发具箱 现在 以前 Web 应 Node.js + Express + Backbone Ruby on Rails Java SSH Apps 开发 Sencha Touch + PhoneGap iOS

单次加载的问题

Page 11: 软件开发实践分享 - 范圣刚的博客 · 我的开发具箱 现在 以前 Web 应 Node.js + Express + Backbone Ruby on Rails Java SSH Apps 开发 Sencha Touch + PhoneGap iOS

导出对象的封装

var Hello = require(‘./singleobject’).Hello

Page 12: 软件开发实践分享 - 范圣刚的博客 · 我的开发具箱 现在 以前 Web 应 Node.js + Express + Backbone Ruby on Rails Java SSH Apps 开发 Sencha Touch + PhoneGap iOS

模块的导出:module.exports

Page 13: 软件开发实践分享 - 范圣刚的博客 · 我的开发具箱 现在 以前 Web 应 Node.js + Express + Backbone Ruby on Rails Java SSH Apps 开发 Sencha Touch + PhoneGap iOS

作为 � class � 导出

Page 14: 软件开发实践分享 - 范圣刚的博客 · 我的开发具箱 现在 以前 Web 应 Node.js + Express + Backbone Ruby on Rails Java SSH Apps 开发 Sencha Touch + PhoneGap iOS

模块复用:node_modules

•package.json � 

•git � 

•npm � init � 

•npm � adduser � 

•npm � publish � 

•本地测试:npm � install � . � -g

测试和发布⾃自⼰己的 Module

Page 15: 软件开发实践分享 - 范圣刚的博客 · 我的开发具箱 现在 以前 Web 应 Node.js + Express + Backbone Ruby on Rails Java SSH Apps 开发 Sencha Touch + PhoneGap iOS

同步和异步

Page 16: 软件开发实践分享 - 范圣刚的博客 · 我的开发具箱 现在 以前 Web 应 Node.js + Express + Backbone Ruby on Rails Java SSH Apps 开发 Sencha Touch + PhoneGap iOS

使用 � Underscore.js

简化同步操作

Page 17: 软件开发实践分享 - 范圣刚的博客 · 我的开发具箱 现在 以前 Web 应 Node.js + Express + Backbone Ruby on Rails Java SSH Apps 开发 Sencha Touch + PhoneGap iOS

异步编程的常见陷阱

Page 18: 软件开发实践分享 - 范圣刚的博客 · 我的开发具箱 现在 以前 Web 应 Node.js + Express + Backbone Ruby on Rails Java SSH Apps 开发 Sencha Touch + PhoneGap iOS

异步操作的工作流

同步遍历⽂文件并读取内容的代码

Page 19: 软件开发实践分享 - 范圣刚的博客 · 我的开发具箱 现在 以前 Web 应 Node.js + Express + Backbone Ruby on Rails Java SSH Apps 开发 Sencha Touch + PhoneGap iOS

改成异步后的代码

Page 20: 软件开发实践分享 - 范圣刚的博客 · 我的开发具箱 现在 以前 Web 应 Node.js + Express + Backbone Ruby on Rails Java SSH Apps 开发 Sencha Touch + PhoneGap iOS

Async � 的工作流控制和任务组织

简化异步数据集和函数集操作的利器

Page 21: 软件开发实践分享 - 范圣刚的博客 · 我的开发具箱 现在 以前 Web 应 Node.js + Express + Backbone Ruby on Rails Java SSH Apps 开发 Sencha Touch + PhoneGap iOS

Async:简化数据集操作

Page 22: 软件开发实践分享 - 范圣刚的博客 · 我的开发具箱 现在 以前 Web 应 Node.js + Express + Backbone Ruby on Rails Java SSH Apps 开发 Sencha Touch + PhoneGap iOS

Async:简化任务集操作

series & parallel

Page 23: 软件开发实践分享 - 范圣刚的博客 · 我的开发具箱 现在 以前 Web 应 Node.js + Express + Backbone Ruby on Rails Java SSH Apps 开发 Sencha Touch + PhoneGap iOS

Async:waterfall

Page 24: 软件开发实践分享 - 范圣刚的博客 · 我的开发具箱 现在 以前 Web 应 Node.js + Express + Backbone Ruby on Rails Java SSH Apps 开发 Sencha Touch + PhoneGap iOS

Async:queue

Page 25: 软件开发实践分享 - 范圣刚的博客 · 我的开发具箱 现在 以前 Web 应 Node.js + Express + Backbone Ruby on Rails Java SSH Apps 开发 Sencha Touch + PhoneGap iOS

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

Page 26: 软件开发实践分享 - 范圣刚的博客 · 我的开发具箱 现在 以前 Web 应 Node.js + Express + Backbone Ruby on Rails Java SSH Apps 开发 Sencha Touch + PhoneGap iOS

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

Page 27: 软件开发实践分享 - 范圣刚的博客 · 我的开发具箱 现在 以前 Web 应 Node.js + Express + Backbone Ruby on Rails Java SSH Apps 开发 Sencha Touch + PhoneGap iOS

异常捕捉和错误处理

Page 28: 软件开发实践分享 - 范圣刚的博客 · 我的开发具箱 现在 以前 Web 应 Node.js + Express + Backbone Ruby on Rails Java SSH Apps 开发 Sencha Touch + PhoneGap iOS

assert

Page 29: 软件开发实践分享 - 范圣刚的博客 · 我的开发具箱 现在 以前 Web 应 Node.js + Express + Backbone Ruby on Rails Java SSH Apps 开发 Sencha Touch + PhoneGap iOS

顺序执行和异常捕捉

Page 30: 软件开发实践分享 - 范圣刚的博客 · 我的开发具箱 现在 以前 Web 应 Node.js + Express + Backbone Ruby on Rails Java SSH Apps 开发 Sencha Touch + PhoneGap iOS

JSON � 解析的异常捕捉

Page 31: 软件开发实践分享 - 范圣刚的博客 · 我的开发具箱 现在 以前 Web 应 Node.js + Express + Backbone Ruby on Rails Java SSH Apps 开发 Sencha Touch + PhoneGap iOS

//Define divider as a syncrhonous functionvar 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/0result = 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);}

同步执⾏行返回错误对象

Page 32: 软件开发实践分享 - 范圣刚的博客 · 我的开发具箱 现在 以前 Web 应 Node.js + Express + Backbone Ruby on Rails Java SSH Apps 开发 Sencha Touch + PhoneGap iOS

异步(嵌套)回调和异常处理

Page 33: 软件开发实践分享 - 范圣刚的博客 · 我的开发具箱 现在 以前 Web 应 Node.js + Express + Backbone Ruby on Rails Java SSH Apps 开发 Sencha Touch + PhoneGap iOS

回调内抛出的异常

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.");}

Page 34: 软件开发实践分享 - 范圣刚的博客 · 我的开发具箱 现在 以前 Web 应 Node.js + Express + Backbone Ruby on Rails Java SSH Apps 开发 Sencha Touch + PhoneGap iOS

未捕获异常的处理

process.on('uncaughtException', function(err) { // handle the error safely console.log(err);});!// the asynchronous or synchronous code that emits the otherwise uncaught errorvar err = new Error('example');throw err;

Page 35: 软件开发实践分享 - 范圣刚的博客 · 我的开发具箱 现在 以前 Web 应 Node.js + Express + Backbone Ruby on Rails Java SSH Apps 开发 Sencha Touch + PhoneGap iOS

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); }});

异步返回错误

Page 36: 软件开发实践分享 - 范圣刚的博客 · 我的开发具箱 现在 以前 Web 应 Node.js + Express + Backbone Ruby on Rails Java SSH Apps 开发 Sencha Touch + PhoneGap iOS

//Definite our Divider Event Emittervar events = require('events');var Divider = function(){ events.EventEmitter.call(this);}; require('util').inherits(Divider, events.EventEmitter);// Add the divide functionDivider.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 errorsvar 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);});!// Dividedivider.divide(4,2).divide(4,0);

使⽤用 Event

Page 37: 软件开发实践分享 - 范圣刚的博客 · 我的开发具箱 现在 以前 Web 应 Node.js + Express + Backbone Ruby on Rails Java SSH Apps 开发 Sencha Touch + PhoneGap iOS

运行和日志

Page 38: 软件开发实践分享 - 范圣刚的博客 · 我的开发具箱 现在 以前 Web 应 Node.js + Express + Backbone Ruby on Rails Java SSH Apps 开发 Sencha Touch + PhoneGap iOS

Linux � 上后台运行

Page 39: 软件开发实践分享 - 范圣刚的博客 · 我的开发具箱 现在 以前 Web 应 Node.js + Express + Backbone Ruby on Rails Java SSH Apps 开发 Sencha Touch + PhoneGap iOS

以生产环境运行

NODE_ENV=production node yourscript.js

Page 40: 软件开发实践分享 - 范圣刚的博客 · 我的开发具箱 现在 以前 Web 应 Node.js + Express + Backbone Ruby on Rails Java SSH Apps 开发 Sencha Touch + PhoneGap iOS

故障恢复和进程管理 � - � forever

https://github.com/nodejitsu/forever

Page 41: 软件开发实践分享 - 范圣刚的博客 · 我的开发具箱 现在 以前 Web 应 Node.js + Express + Backbone Ruby on Rails Java SSH Apps 开发 Sencha Touch + PhoneGap iOS

log4js

https://github.com/nomiddlename/log4js-node

Page 42: 软件开发实践分享 - 范圣刚的博客 · 我的开发具箱 现在 以前 Web 应 Node.js + Express + Backbone Ruby on Rails Java SSH Apps 开发 Sencha Touch + PhoneGap iOS

数据库:Mongoose � V3

•从 � Node.js � MongoDB � driver � 迁移到 � Mongoose

http://mongoosejs.com

http://mongodb.github.io/node-mongodb-native/

Page 43: 软件开发实践分享 - 范圣刚的博客 · 我的开发具箱 现在 以前 Web 应 Node.js + Express + Backbone Ruby on Rails Java SSH Apps 开发 Sencha Touch + PhoneGap iOS

store.addOneDocument('event_security', event_signin, function(err, event_result) {});

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);}

对 MongoDB node.js driver 的简单封装

Page 44: 软件开发实践分享 - 范圣刚的博客 · 我的开发具箱 现在 以前 Web 应 Node.js + Express + Backbone Ruby on Rails Java SSH Apps 开发 Sencha Touch + PhoneGap iOS

'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

Page 45: 软件开发实践分享 - 范圣刚的博客 · 我的开发具箱 现在 以前 Web 应 Node.js + Express + Backbone Ruby on Rails Java SSH Apps 开发 Sencha Touch + PhoneGap iOS

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等

Page 46: 软件开发实践分享 - 范圣刚的博客 · 我的开发具箱 现在 以前 Web 应 Node.js + Express + Backbone Ruby on Rails Java SSH Apps 开发 Sencha Touch + PhoneGap iOS

总结

•操作系统: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

Page 47: 软件开发实践分享 - 范圣刚的博客 · 我的开发具箱 现在 以前 Web 应 Node.js + Express + Backbone Ruby on Rails Java SSH Apps 开发 Sencha Touch + PhoneGap iOS

开发小故事

•调试:node-inspector(webkit+远程调试) � 

•‘use � strict’:全局变量; � 

•npm � install � <package_name> � —save � : � 自动更新 � package.json � 

•Canvas � -> � PhantomJS � 

•消息队列:kue � -> � RabbitMQ

Page 48: 软件开发实践分享 - 范圣刚的博客 · 我的开发具箱 现在 以前 Web 应 Node.js + Express + Backbone Ruby on Rails Java SSH Apps 开发 Sencha Touch + PhoneGap iOS

我为什么使用 � Node.js � ?

我的开发⼯工具箱 现在 以前

Web 应⽤用 Node.js + Express + Backbone

Ruby on Rails Java SSH

Apps 开发 Sencha Touch + PhoneGap

iOS Android

⽹网络和系统编程 Node.js C++

脚本和⼩小⼯工具 Node.js, Ruby Ruby, C++

数据库 MongoDB, redis SQL Server, Oracle, MySQL

数据交换 JSON ⼆二进制的 protocol,http urlencode

简单⾼高效

Page 49: 软件开发实践分享 - 范圣刚的博客 · 我的开发具箱 现在 以前 Web 应 Node.js + Express + Backbone Ruby on Rails Java SSH Apps 开发 Sencha Touch + PhoneGap iOS

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/

Page 50: 软件开发实践分享 - 范圣刚的博客 · 我的开发具箱 现在 以前 Web 应 Node.js + Express + Backbone Ruby on Rails Java SSH Apps 开发 Sencha Touch + PhoneGap iOS

不明觉厉

•examples � -> � Mocha � 

•express � -> � kraken � 

•callback � -> � Promise � + � Q.js � 

•libuv,v8, � Linux � C++

Page 51: 软件开发实践分享 - 范圣刚的博客 · 我的开发具箱 现在 以前 Web 应 Node.js + Express + Backbone Ruby on Rails Java SSH Apps 开发 Sencha Touch + PhoneGap iOS

Thank � you!

Page 52: 软件开发实践分享 - 范圣刚的博客 · 我的开发具箱 现在 以前 Web 应 Node.js + Express + Backbone Ruby on Rails Java SSH Apps 开发 Sencha Touch + PhoneGap iOS

http://gitlab.org/

Page 53: 软件开发实践分享 - 范圣刚的博客 · 我的开发具箱 现在 以前 Web 应 Node.js + Express + Backbone Ruby on Rails Java SSH Apps 开发 Sencha Touch + PhoneGap iOS

Performance

Page 54: 软件开发实践分享 - 范圣刚的博客 · 我的开发具箱 现在 以前 Web 应 Node.js + Express + Backbone Ruby on Rails Java SSH Apps 开发 Sencha Touch + PhoneGap iOS

ab -n 10000 -c 1000

Page 55: 软件开发实践分享 - 范圣刚的博客 · 我的开发具箱 现在 以前 Web 应 Node.js + Express + Backbone Ruby on Rails Java SSH Apps 开发 Sencha Touch + PhoneGap iOS

Node.js vs. Apache + PHP

Node.js

Apache + PHP

1M20K HTTP REQUEST

Page 56: 软件开发实践分享 - 范圣刚的博客 · 我的开发具箱 现在 以前 Web 应 Node.js + Express + Backbone Ruby on Rails Java SSH Apps 开发 Sencha Touch + PhoneGap iOS

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

Page 57: 软件开发实践分享 - 范圣刚的博客 · 我的开发具箱 现在 以前 Web 应 Node.js + Express + Backbone Ruby on Rails Java SSH Apps 开发 Sencha Touch + PhoneGap iOS

Node.js vs. Go

Page 58: 软件开发实践分享 - 范圣刚的博客 · 我的开发具箱 现在 以前 Web 应 Node.js + Express + Backbone Ruby on Rails Java SSH Apps 开发 Sencha Touch + PhoneGap iOS

Node.js vs. Go

• Go 的数据处理优于 Node.js(⽐比如运⾏行冒泡排序)

• Node.js 的 HTTP 处理和伸缩性⽐比 Go 更有效率

•团队经验(JavaScript Vs. ⼀一种全新的语⾔言)