深入浅出Node.js 的读书笔记
第一章 Node简介
简单介绍了一下node的历史09年/Ryan Dahl和特点异步IO,结构等等
第二章 模块机制
- CommonJS的规范 引用规范require、定义exports.xx、标识等
- 模块实现 模块加载顺序缓存-核心-文件文件定位顺序**.js-.json.node** 核心模块是用C/C++编写的
- 包和npm 包其实就是一些人写的实现某些功能的代码集合package.json、bin等等文件在里面 npm就是下载这些包的工具,很方便,但是也有问题,代码参齐不全,可以通过github的星来判断好坏
- 前后端共用模块 其实就是都是用一种语言来写前后端,所以就比较方便的意思
第三章 异步I/O
- 为什么要用异步I/O 我之前是写php的,就是没有并发,有时候就很不爽,node的异步就可以用于并发,多个请求的时间从M+N->Max(M,N)
- 现状 比较阻塞I/O和非阻塞I/O
- Node的异步I/O 事件循环进程启动的时候不断去看有没有事件Tick需要处理
- 事件驱动与高性能服务器
第四章 异步编程
- 函数式编程 高阶函数把函数当参数和返回值 function foo(x, bar) { return bar(); }
- 异步编程的优势和缺点 优势就是资源分配好,缺点就是对于异常的处理、嵌套过深、没有sleep、多线程对于写代码的也需要适应
- 异步编程的解决方案 发布/订阅模式 Promise/Deferred模式其实就是jquery里面的ajax方法,有success和error、complete等 流程控制库next、async
- 异步并发控制 bagpip通过设定限制来阻止过多的并发
内存控制
V8在64位下面只可以使用最多1.4G的内存,很少,所以内存的控制是很有必要的
- V8的垃圾回收机制 分代式垃圾回收机制新生代内存优先回收,老生代的基本不回收,存在各种回收算法 Scavenge算法:简单来说就是分为from区和to去,回收的时候检查from有哪些是存活的,丢到to那边去,然后交换两个区,最后再清空原from,但是这个办法太浪费空间了 Mark-Sweep & Mark-Compact算法:其实就是做标记,需要回收的时候回收标记好的,这种会引起碎片化的问题
- 高效使用内存 大概说了一下作用域、闭包、主动释放delete
- 内存指标 查看进程使用的内存占用:process.memoryUsage()、查看系统使用的内存占用:os.totalmem()/os.freemem()
- 内存泄露 内存泄漏也称作“存储渗漏”,用动态存储分配函数动态开辟的空间,在使用完毕后未释放,结果导致一直占据该内存单元。直到程序结束。(其实说白了就是该内存空间使用完毕之后未回收)即所谓内存泄漏 不要随便就用内存做缓存、关注队列、排查node-heapdump、node-memwatch
理解Buffer
- buffer结构 其实buffer可以简单理解为一个Array对象,可以操作字节,结构上就是C++和javascript的结合,最大8K,8K内为小对象 内存分配full、partial、empty
- buffer的转化 字符串转buffer new Buffer(str,[encoding]) buffer转字符串 buf.toString([encoding],[start],[end])
- buffer的拼接 因为外国人使用的是英文,在拼接上面有天然的优势,但是我们使用的是英文,所以必须小心处理这个问题,正确的办法应该是用数组保存所以buffer对象的长度,再用Buffer.concat()来拼接 var chucks = []; var size = 0; res.on('data', function(chuck) { chucks.push(chuck); size += chuck.length; }); res.on('end', function() { var buf = Buffer.concat(chucks, size); var str = iconv.decode(buf, 'utf8'); console.log(str); })
- buffer和性能 其实buffer的转化在无时无刻进行着
网络编程
其实这一章并不能说说node里面的知识,而是开发web都需要的知识
- TCP 创建TCP服务器和一些TCP事件
- UPD 创建UPD服务器和一些UPD事件
- HTTP HTTP请求 HTTP响应 HTTP事件
- WebSocket
- 网络服务与安全 TLS/SSL 其实就是一个服务器和客户端直接的认证,还有HTTPS证书等
构建Web应用
- 基础功能 请求方法get/post/delete/put 路径解析HTTP_Parser
- cookie 放在request.head里面
- session 安全性,虽然是放在服务器,但是在是通过cookie来认证的,所以还是有危险性的,类似xss漏洞
- 缓存
- basic认证
- 数据上传 表单数据:判断Content-Type:application/x-www-form-urlencoded JSON:Content-Type:application/json;charset=utf-8 XML:Content-Type:application/xml;charset=utf-8 附件上传:有分隔符,可以使用formidable模块来直接处理 内存限制:写方法对上传上来的大小做限制,超过则显示413 CSRF:跨站请求伪造,可以在表单数据里面添加一个csrf的认证字段
- 路由解析 其实这个里面比较关键的就是静态文件的请求路由,一般在路由级别加到一层,这样就可以避免请求的时候都去扫描所有文件了
- MVC 其实就是路由怎么去映射,这个express里面就做得不错
- 中间件 一些每个请求都需要去申请的功能,例如log、session,用一些别人写好的代码片段来给我们实现
- 页面渲染 就是告诉页面,我这里需要用什么类型来解析是html,txt,还是json 模板,我们有些数据是动态的,需要在页面展示的时候输出,这个时候就需要用到模板技术了,这里大概说了一下模板技术是怎么实现的,没认真看,我是直接用了ejs,express里面默认的模板技术
玩转进程
一开始介绍了一下进程的发展
- 多进程架构 父进程复杂分配,子进程复杂具体的业务,自带的child_process就可以实现了,下面的代码就是创建了两个子进程在sub.js里面
var cp = require('child_process');
var child1 = cp.fork(__dirname + '/sub.js');
var child2 = cp.fork(__dirname + '/sub.js');
var server = require('net').createServer();
server.on('connection', function(socket) {
socket.end('父句柄');
});
server.listen(1337, function() {
child1.send('server',server);
child2.send('server',server);
server.close();
})
- 集群稳定之路 一个关键就是自动重启
var fork = require('child_process').fork;
var cpus = require('os').cpus();
var limit = 10;
// 时间单位
var during = 60000;
var restart = [];
var isTooFrequently = function() {
// 记录重启时间
var time = Date.now();
console.log('现在的时间是:' + time);
var length = restart.push(time);
console.log('重启数组长度为:' + length);
if (length > limit) {
// 取出最后10个记录
restart = restart.slice(limit * -1);
}
// 长度不大于限制 and 最新一次重启时间和第一次重启时间直接的间隔不大于限定的时间
console.log('第一次启动和现在这一次的时间间隔为' + (restart[restart.length - 1] - restart[0]));
return restart.length >= limit && restart[restart.length - 1] - restart[0] < during;
}
var server = require('net').createServer();
server.listen(1337);
var workers = {};
var createWorker = function() {
if (isTooFrequently()) {
console.log('不允许重启了');
process.emit('giveup', restart.length, during);
return;
}
var worker = fork(__dirname + '/worker.js');
worker.on('message', function(message) {
if (message.act === 'suicide') {
createWorker();
}
})
worker.on('exit', function() {
console.log('worker' + worker.pid + ' exited');
delete workers[worker.pid];
});
worker.send('server', server);
workers[worker.pid] = worker;
console.log('Create worker . pid:' + worker.pid);
};
// 循环创建
for (var i = 0; i < cpus.length; i++) {
createWorker();
};
// 如果进程自己退出,就让所有的工作进程退出
process.on('exit', function() {
for (var pid in workers) {
workers[pid].kill();
}
})
```
子进程
var http = require('http');
var server = http.createServer(function(req, res) {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('headled by child,pid is ' + process.pid + '\n');
throw new Error('throw exception');
});
var worker;
process.on('message', function(m, tcp) {
if (m === 'server') {
worker = tcp;
worker.on('connection', function(socket) {
server.emit('connection', socket);
});
}
});
process.on('uncaughtException', function(err) {
process.send({ act: 'suicide' });
// 停止接收新的连接
worker.close(function() {
// 所以已有连接断开后,退出进程
process.exit(1);
});
setTimeout(function() {
process.exit(1);
}, 5000);
})
- Cluster模块
更好的创建和管理进程
### 测试和项目工程化
这两章放在一起,其实是我没怎么仔细看,我现在对于Node的理解其实还是很基础,所以这些其实对于我现阶段也是意义不大。所以就不说了
### 总结
学习node说长不长说短不短也有几个月了,期间就基本就是在看书,没做太大的实践,其实这样不好,没有成就感,接下来是要思考自己做一个小项目了。