读书笔记: 深入浅出node.js
>> 深入浅出node.js
node.js是c++编写的js运行环境
浏览器: 渲染引擎 + js引擎
后端的js运行环境
node.js用google v8引擎,同时提供很多系统级的API(文件操作 网络编程...)
node.js采用事件驱动 异步编程,为网络服务而设计
浏览器端的js有各种安全限制
node.js提供的多数API都是基于事件的,异步的风格。
node.js的优点:充分利用系统资源,执行代码不会被阻塞以等待某个操作的完成;这个设计非常适合后端的网络服务编程。通过事件注册,回调,可以提高资源的利用率,改善性能。
为方便服务器开发,node.js的网络模块有很多:
HTTP, HTTPS, DNS, NET, UDP, TLS等,有助于快速构建web服务器。
node.js的特点:事件驱动,异步编程,单进程,单线程。
node.js内部通过单线程高效率地维护事件循环队列,没有太多的资源占用和上下文切换。
单核性能出色的node.js如何利用多核CPU呢?建议运行多个node.js进程,用某种通信协议协调各项任务。
javascript的匿名函数和闭包特性非常适合事件驱动,异步编程。
node.js最擅长的事情是与其他服务通信。因为node.js是基于事件的无阻塞的,所以非常适合处理并发请求;缺点:node.js是相对新的一个开源项目,所以不太稳定,一直的变,而且缺少足够多的第三方库支持。
知名项目托管网站GitHub也尝试了node应用(NodeLoad). 使用node.js的项目还有MyFox
Node库socket.io
>> node.js的安装和配置
window版已经编译好的node.js安装包 nodejs.msi,安装后在Node命令行环境输入 node -v,有版本号输出,则说明安装好了。
NPM(Node Package Manager)NodeJs的包管理器。通过NPM我们可以安装Nodejs的第三方库。
Unix/Linux安装NPM:
curl http://npmjs.org/install.sh | sudo sh
获取shell脚本,交给sh命令以sudo权限执行。
安装完之后 输入 npm 回车,如输出帮助信息 则表明安装成功。
用NPM安装第三方库:如underscore
npm install underscore
由于一些特殊的网络环境用npm安装第三方库时,容易卡死;可以通过一个镜像的npm资源库来安装,如:
npm --registry "http://npm.hacknodejs.com/" install underscore
设置为默认npm资源库:
npm config set registry "http://npm.hacknodejs.com" , 设置之后就可以直接 npm install underscore
window下安装NPM:
下载较新的nodejs.msi(v0.10...)安装包并安装后,npm默认也安装了。
命令行下输入 npm回车即可查看npm的帮助信息。
npm install underscore
>> node.js的模块机制
CommonJs规范:构建javascript在包括服务器,桌面,命令行的生态系统。
node.js实现了用require方法引入其他模块,同时npm也基于CommonJs规范定义了包规范,实现了依赖管理和包的自动安装。
> 模块的定义和使用
circle.js:
var PI = Math.PI;
exports.area = function(r){
return PI*r*r
}
exports.length = function(r){
return 2*PI*r
}
app.js:
var circle = require('./circle.js');
console.log('the area of circle of radius 4 is ' + circle.area(4) );
> 模块的载入策略
node.js的模块分为2类:原生(核心)模块 和 文件模块
原生模块在node.js源代码编译时就编译进了二进制执行文件,加载速度最快;文件模块是动态加载的,相对慢。
node.js对require后的原生模块和文件模块都有缓存,第二次require时,从缓存获得,不会有重复的开销。
node.js加载文件模块的工作是有原生模块module来实现和完成的。
文件模块按后缀又分为3类:
.js 通过fs模块同步读取js文件并执行
.node c/c++编写的addon, 通过dlopen方法加载
.json 读取文件 用JSON.parse方法解析。
node.js对app.js这类文件模块的加载过程:
1. 对文件内容进行头尾包装,变为模块定义的形式。如包装后变为:
(function(exports, require, module, __filename, __dirname){
var circle = require('./circle.js');
console.log('the area of circle of radius 4 is ' + circle.area(4) );
})
包装后用vm模块的runInThisContext方法执行这个function对象,并传入对应的实参。
require('./circle.js'); //在这里circle.js模块经历 载入--编译--缓存的过程,最后返回module对象exports。
> require方法的文件查找策略
node.js有原生模块和3种文件模块
先在文件模块缓存区查找--若没有-->看看是否原生模块(是,则到原生模块缓存区查找,没找到则加载之并缓存)--若非原生模块--则查找文件模块----根据后缀 载入文件模块并缓存----最后返回exports
require(..)方法的参数:
1. http, fs, path等原生模块
2. ./mod或../mod,相对路径的文件模块
3. /pathtomodule/mod 绝对路径的文件模块
4. mod 非原生模块的文件模块
module path这个概念:
每一个被加载的文件模块,在创建模块对象时,这个模块便会有一个paths属性,其值是根据当前路径计算得到的路径数组。如:
modulepaths.js:
console.log(module.paths);
把modulepaths.js放到 f:\pan\lm\目录下,在命令行切换到该目录,执行 node modulepaths.js,输出:
[ 'F:\\pan\\lm\\node_modules',
'F:\\pan\\node_modules',
'F:\\node_modules' ]
查找路径生成规则很明显,此外还有一个全局的module path: 当前node执行文件的相对目录 和 环境变量中设置的HOME ,NODE_PATH
简而言之,若require绝对路径的文件,查找时不会遍历每一个node_modules目录,速度最快。
对module path数组的每条路径都执行某个查找过程。
>> 包结构
一个符合CommonJs规范的包结构应该如下:~~node_modules
1. 一个package.json文件应该存在于包的顶级目录下。
2. 二进制文件应包含在bin目录下
3. javascript代码也应该在bin目录下
4. 文档应该在doc目录下
5. 单元测试应该在test目录下
module paths路径数组中,在每条路径的查找过程中,在尝试添加扩展名也没找到目标文件时,会尝试将当期路径当做包来加载,从package.json中获取信息。读取package.json的main字段。
package.json的字段:
1. name 包名 需要在npm上是唯一的,不能包含空格。
2. description 包简介
3. version 版本号 x.y.z 用于版本控制的场景
4. keywords 关键字数组,用于npm分类搜素
5. maintainers 包维护者的数组 数组元素是形如 {name:..,email:.., web:..}的对象
6. contributors 包贡献者的数组,第一个元素是包作者,形如{name:..,email:..}
7. bugs 一个可以提交bug的url地址
8. license 包所使用的许可证数组 数组元素如{type:...,url:..}
9. repositories 托管源代码的地址数组
10. dependencies 当前包需要的依赖 这个属性很重要,NPM会通过它自动加载依赖包。
包符合CommonJs规范后,就可以发布,输入命令
npm publish <folder>
(需要先注册一个npm账号 : npm adduser)
若用户要使用npm上的包,可以执行:
npm install <package>
本地方式安装包:
从github手动下载包,在命令行下转到包的目录下 执行:
npm install <package.json>
--------------------------------------------------------
node.js中的js模块文件和script标签加载的js文件的区别:node.js的模块文件中声明的变量是在闭包内的,不会污染全局环境,需要被外部调用的接口都挂在exports上。
>> node.js的事件机制
node.js的特点:Evented I/O for V8 javascript (基于V8引擎的事件驱动I/O)
Event模块(events.EventEmitter)是一个简单的事件监听器模式的实现。具有方法:
addListener/on, once, removeListener, removeAllListeners, emit...
node.js的事件与前端Dom树上的事件不同, 不存在冒泡和逐层捕获等行为,自然也没有 preventDefault(), stopPropagation(), stopImmediatePropagation()等处理事件传递的方法
事件侦听器模式也是一种事件钩子(hook)机制,利用事件钩子导出内部数据和状态给外部调用者。
var options = {
host: 'www.google.com',
port: 80,
path: '/upload',
method: 'POST'
};
var req = http.request(options, function(res){
console.log('STATUS: ' + res.statusCode);
console.log('HEADERS: ' + JSON.stringfy(res.headers));
res.setEncoding('utf8');
res.on('data', function(chunk){
console.log('BODY:' + chunk);
});
});
req.on('error', function(e){
console.log('problem with request: ' + e.message);
});
//write data to request body
req.write('data\n');
req.write('data2\n');
req.end();
注:如对于一个事件添加超过10个监听器,会得到一条警告,因为设计者认为侦听器太多,可能导致内存泄漏。调用这个语句可以取消10个侦听器的限制: emitter.setMaxListener(0);
若运行时的错误触发了error事件,会交给error侦听器处理,若没有设置error侦听器,则抛出异常,若异常没有被捕获,将会引起退出。
> 如何继承event.EventEmitter类,如node.js中流对象继承EventEmitter类:
//类式继承
function Stream(){
event.EventEmitter.call(this);
}
util.inherits(Stream, event.EventEmitter);
//util.inherits应该是如下这样实现原型链的
util.inherits = function(subClass, superClass){
function F(){}
F.prototype = superClass.prototype;
subClass.prototype = new F;
subClass.prototype.constructor = subClass;
}
> 多事件之间的协作
在大应用中,数据源和web服务器分离是必然的。好处有:相同数据源开发各种丰富的客户端程序,从多个数据源拉取数据,渲染到客户端。
node.js擅长同时并行发起对多个数据源的请求。
并行请求:
api.getUser('username', function(profile){
// got the profile
});
api.getTimeline('username', function(timeline){
// got the timeline
});
api.getSkin('username', function(skin){
// got the skin
});
这里存在一个问题:请求可以并行发出,但是如何控制回调函数的执行顺序?
若改为这样:
api.getUser('username', function(profile){
api.getTimeline('username', function(timeline){
api.getSkin('username', function(skin){
// todo
});
});
});
这将导致请求不能并行发出。
node.js没有原生支持多事件之间协调的方法,需要借助第三方库。如:
var proxy = new EventProxy();
//all方法作用:侦听完事件后,执行回调,并将侦听接收到的参数传入回调中
proxy.all('profile', 'timeline', 'skin', function(profile, timeline, skin){
//todo
});
api.getUser('username', function(profile){
proxy.emit('profile', profile); //触发事件profile,并传入实参profile
});
api.getTimeline('username', function(timeline){
proxy.emit('timeline', timeline);
});
api.getSkin('username', function(skin){
proxy.emit('skin', skin);
});
解决多事件协作的另一种方案:Jscex(代码可以用同步的思维去写,异步方式执行)
如:
var data = $await(Task.whenAll({
profile: api.getUser('username'),
timeline: api.getTimeline('username'),
skin: api.getSkin('username')
}));
//使用:data.profile, data.timeline, data.skin
// todo
> 利用事件队列解决雪崩问题
雪崩问题:指在缓存失效的情景下,大量的并发访问同时涌入数据库查询,数据库无法承受,进而导致网站整体很慢。
看看加一个状态锁的解决方案:
var status = 'ready';
var select = function(callback){
if(status ==='ready'){
status = 'pending'; //上锁
db.select('SQL', function(results){
callback(result);
status = 'ready'; //解锁
});
}
}
连续多次调用select方法时,锁定期间有的select会不被处理;所以应该用事件队列的方式来解决。
var proxy = new EventProxy();
var status = 'ready';
var select = function(callback){
proxy.once('selected', callback);//将所有请求的回调都压入事件队列中
if(status === 'ready'){
status = 'pending';
db.select('SQL', function(result){
proxy.emit('selected', result);
status = 'ready';
});
}
}
>> node.js的异步I/O实现
同步:程序中的后续任务都需要等待I/O的完成,等待的过程中无法充分利用CPU。
实现I/O并行,充分利用CPU的方式有2种:多线程但进程, 单线程多进程
getFile('file_path'); //耗时m
getFileFromNet('url'); //耗时n
同步I/O的话,需要m+n, 异步I/O的话,需要max(m,n)
异步I/O在分布式的环境中很重要,能明显改善性能。
> 异步I/O与轮询技术
进行非阻塞的I/O操作时,要读取到完整数据,应用程序要多次轮询,才能确保数据读取完成,然后进行下一步。轮询技术的缺点是应用程序主动多次调用,占用较多CPU时间片。
>> node.js的异步I/O模型
fs.open = function(path, flags, mode, callback){
callback = arguments[arguments.length - 1];
if( (typeof callback) !== 'function' ){
callback = noop;
}
mode = modeNum(mode, 438); /* 438 = 0666 */
binding.open(pathModule._makeLong(path), stringToFlags(flags), mode, callback);
}
>> string buffer
var fs = require('fs');
var rs = fs.createReadStream('testdata.md');
var data = '';
rs.on('data', function(trunk){
data += trunk; //trunk 是一个buffer对象
});
rs.on('end', function(){
console.log(data);
});
npm install bufferhelper;
bufferconcate.js:
var http = require('http');
var BufferHelper = require('bufferhelper');
http.createServer(function(req, res){
var bufferHelper = new BufferHelper();
req.on('data', function(chunk){
bufferHelper.concat(chunk);
});
req.on('end', function(){
var html = bufferHelper.toBuffer().toString();
res.writeHead(200);
res.end(html);
});
});
>> connect模块(node.js web框架)
中间件的流式处理
var app = connect();
//middleware
app.use(connect.staticCache());
app.use(connect.static(__diranme + '/public') );
app.use(connect.cookieParser());
app.use(connect.session());
...
app.use(function(req, res, next){
//中间件
});
app.listen(3001);
connect用use方法注册中间件到中间件队列中。
读书笔记: 深入浅出node.js的更多相关文章
- 《Node入门》读书笔记——用Node.js开发一个小应用
Android APP的开发告一段落,一个稳定的.实现了基本功能的APP已经交付用户使用了!我和老板交流了下,接下来准备转战Node.js了,而且一部分前端的功能也要做进去!哈哈哈~~~接下来要朝一个 ...
- 《深入浅出Node.js》第6章 理解 Buffer
@by Ruth92(转载请注明出处) 第6章 理解 Buffer ✁ 为什么需要 Buffer? 在 Node 中,应用需要处理网络协议.操作数据库.处理图片.接收上传文件等,在网络流和文件的操作中 ...
- 《深入浅出Node.js》第7章 网络编程
@by Ruth92(转载请注明出处) 第7章 网络编程 Node 只需要几行代码即可构建服务器,无需额外的容器. Node 提供了以下4个模块(适用于服务器端和客户端): net -> TCP ...
- 深入浅出Node.js(一):什么是Node.js
Node.js从2009年诞生至今,已经发展了两年有余,其成长的速度有目共睹.从在github的访问量超过Rails,到去年底Node.jsS创始人Ryan Dalh加盟Joyent获得企业资助,再到 ...
- 一个月时间整理《深入浅出Node.js》
今天终于把朴灵老师写的<深入浅出Node.js>给学习完了, 这本书不是一本简单的Node入门书籍,它没有停留在Node介绍或者框架.库的使用层面上,而是从不同的视角来揭示Node自己内在 ...
- node.js系列笔记之node.js初识《一》
node.js系列笔记之node.js初识<一> 一:环境说明 1.1 Linux系统CentOS 5.8 1.2 nodejs v0.10.15 1.3 nodejs源码下载地址 htt ...
- 深入浅出Node.js(一):什么是Node.js(转贴)
以下内容转自:http://www.infoq.com/cn/articles/what-is-nodejs/ 作者:崔康 [编者按]:Node.js从2009年诞生至今,已经发展了两年有余,其成长的 ...
- 《深入浅出node.js(朴灵)》【PDF】下载
<深入浅出node.js(朴灵)>[PDF]下载链接: https://u253469.pipipan.com/fs/253469-230062563 内容简介 <深入浅出Node. ...
- 笔记:Node.js 的 Buffer 缓冲区
笔记:Node.js 的 Buffer 缓冲区 node.js 6.0 之前创建的 Buffer 对象使用 new Buffer() 构造函数来创建对象实例,但权限很大,可以获得敏感信息,所以建议使用 ...
随机推荐
- winfrom运用webservice上传文件到服务器
winfrom做文件上传的功能显然没有BS的简单,本实例是运用了webservice获取二进制流转换的字符串.然后,解析字符串,把流文件再转成pdf. webservice 里面的代码为下: [Web ...
- javaScript 工作必知(七) 对象继承
对象继承inherit var o = { r: 1 }; var c = function f() { }; c.prototype = o; c.r = 3; alert(o.r);//被继承的属 ...
- android webview点击返回键回到上一个html
android webview点击返回键返回上一个html @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if ( ...
- C++/C# 最基本的Marshal和Ptr
Vidyo32.VidyoClientInEventLogin Login = new Vidyo32.VidyoClientInEventLogin(); Login.portalUri = thi ...
- PHP学习遇到的问题
使用php ob_start()模板生成html 内容无法撑开 生成后主要的内容 <body><warp><main> 生成后这个几个节点总是固定大小,不能被内容撑 ...
- SDOI2008 Sandy的卡片( 后缀数组 )
求出后缀数组, 然后二分答案, 对height数组分组检验答案. 时间复杂度O(|S| log|S|) ------------------------------------------------ ...
- Web存储(Web Storage)介绍
Web存储即在客户端存储数据. 在没有Web Storage之前,是通过cookie来在客户端存储数据的.但是由于 浏览器能存cookie数比较少.如IE8,Firefox,opera每个域可以保存的 ...
- Jsoup代码解读之五-实现一个CSS Selector
Jsoup代码解读之七-实现一个CSS Selector 当当当!终于来到了Jsoup的特色:CSS Selector部分.selector也是我写的爬虫框架webmagic开发的一个重点.附上一张s ...
- [置顶] js模板方法的思路及实现
在js中如何实现设计模式中的模板方法? 思路的产生必然要求熟悉js,如何实现?就很简单了,都知道在js中如果定义两个相同名称的方法,前一个方法就会被后一个方法覆盖掉,使用此特点就可以实现模板方法. 例 ...
- android 向serverGet和Post请求的两种方式,android向server发送文件,自己组装协议和借助第三方开源
一个适用于Android平台的第三方和apache的非常多东西类似,仅仅是用于Android上 我在项目里用的是这个 https://github.com/loopj/android-async-ht ...