JavaShuo
栏目
标签
[书籍精读]《深刻浅出Node.js》精读笔记分享
时间 2020-05-21
标签
书籍
精读
深刻浅出Node.js
笔记
分享
栏目
Node.js
繁體版
原文
原文链接
写在前面
书籍介绍:本书由首章Node介绍为索引,涉及Node的各个方面,主要内容包含模块机制的揭示、异步I/O实现原理的展示、异步编程的探讨、内存控制的介绍、二进制数据Buffer的细节、Node中的网络编程基础、Node中的Web开发、进程间的消息传递、Node测试以及经过Node构建产品须要的注意事项。
个人简评:这是一本可贵的好书,这本书理论和实践结合的很好。若是你是一个纯前端的开发者,这本书能够读读开拓些视野,若是你是一个全栈的开发者,这本书做为入门和深刻后端也很不错,推荐拜读。
!!文末有pdf书籍、笔记思惟导图、随书代码打包下载地址,须要请自取!阅读[书籍精读系列]全部文章,请移步:
推荐收藏-JavaScript书籍精读笔记系列导航
第一章 Node简介
1.1.Node的诞生历程
2009年3月, Ryan Dahl
1.2.Node的命名与起源
别名 Nodejs、 NodeJS、 Node.js
找到了设计高性能, Web服务器的几个要点: 事件驱动、非阻塞I/O
JavaScript 高性能、符合事件驱动、没有历史包袱
构建网络应用的一个基础框架
1.3.Node给JavaScript带来的意义
浏览器中除了V8做为JavaScript引擎外,还有一个WebKit布局引擎
浏览器经过事件驱动来服务界面上的交互, Node经过过事件驱动来服务I/O
1.4.Node的特色
异步I/O、事件与回调函数、单线程、跨平台
单线程:弱点1:没法利用多核CPU;弱点2:错误会引发整个应用退出,应用的健壮性值得考验;弱点3:大量计算占用CPU致使没法继续调用异步I/O;Node采用与Web Workers相同的思路来解决单线程中大计算量的问题:child_process;
跨平台:在操做系统与Node上层模块系统之间构建了一层平台层架构,即libuv;Node的第三方C++模块也能够借助libuv实现跨平台;
1.5.Node的应用场景
I/O密集型:面向网络且擅长并行I/O
是否不擅长CPU密集型业务:采用使用多线程的方式进行计算;经过编写C++扩展的方式更高效利用CPU;
与遗留系统和平共处
分布式应用:数据平台、数据库集群
1.6.Node的使用者
先后端编程语言环境统一
Node带来的高性能I/O用于实时应用
并行I/O使得使用者能够更高效的利用分布式环境
并行I/O,有效利用稳定接口提高Web渲染能力
云计算平台提供Node支持
游戏开发领域
工具类应用
第二章 模块机制
大体经历了工具类库、组件库、前端框架、前端应用的变迁
2.1.CommonJS规范
CommonJS的出发点:规范薄弱,如下缺陷(没有模块系统、标准库较少、没有标准接口、缺少包管理系统)
CommonJS的模块规范:主要分为模块引用、模块定义和模块标识3个部分
2.2.Node的模块实现
优先从缓存加载:浏览器仅仅缓存文件,而Node缓存的是编译和执行以后的对象
路径分析和文件定位:Node 会按.js、.json、.node的次序补足扩展名
模块编译:每个编译成功的模块都会将其文件路径做为索引缓存在Module._cache对象上;在编译的过程当中,Node对获取的JavaScript文件内容进行头尾包装;(function(exports, require, module, __filename, __dirname) {n, 在尾部添加了n});C/C++模块,Node调用process.dlopen()方法进行加载和执行;
2.3.核心模块
JavaScript核心模块的编译过程:C/C++文件放在Node项目的src目录下,JavaScript文件存放在lib目录下;编译程序须要将全部的JavaScript模块文件编译为C/C++代码;
C/C++核心模块的编译过程:Node在启动时,会生成一个全局变量process,并提供Binding()方法来协助内建模块
2.4.C/C++扩展模块
说明:JavaScript的一个典型弱点就是位运算;*nix下经过g++/gcc等编译器编译为动态连接共享对象文件.so,Windows下则须要经过VisualC++的编译器编译为动态连接库文件.dll;
前提条件:GYP项目生成工具、V8引擎C++库、libuv库、Node内部库、等等
C/C++扩展模块的编写:普通的扩展模块与内建模块的区别在于无需将源代码编译进Node,而是经过dlopen()方法动态加载
C/C++扩展模块的编译:写好.gyp项目文件是除编码外的头等大事;编译过程会根据平台不一样,分别经过make或者vcbuild进行编译;
C/C++扩展模块的加载:require()方法经过解析标识符、路径分析、文件定位,而后加载执行便可;加载.node文件实际上经历了两个步骤,第一个步骤是掉用uv_dlopen方法去打开动态连接库,第二个步骤调用uv_dlsym()方法找到动态连接库中经过NODE_MODULE宏定义的方法地址;
2.5.模块调用栈
JavaScript核心模块主要扮演的职责有两类:一类是做为C/C++内建模块的封装层和桥接层,供文件模块调用;一类是纯粹的功能模块,不须要跟底层打交道
2.6.包和NPM
包描述文件与NPM:包规范的定义能够帮助Node解决依赖包安装的问题;NPM实际须要的字段主要有name、version、description、keywords、repositories、author、bin、main、scripts、engines、dependencies、devDependencies;
NPM经常使用功能:NPM帮助完成了第三方模块的发布、安装和依赖;查看帮助npm、分析包npm ls;
局域NPM:可以享受到NPM上众多的包,同时对本身的包进行保密和限制
NPM潜在问题:开发人员水平不一,包质量也参差不齐;NPM模块首页上的依赖榜能够说明模块的质量和可靠性;GitHub上项目的观察者数量和分支数量从侧面反映模块的可靠性和流行度;计划引入CPAN社区中的Kwalitee风格来让模块进行天然排序;
2.7.先后端共用模块
模块的侧重点:前端经过网络加载代码,瓶颈在于带宽,后端从磁盘加载,瓶颈在于CPU和内存等资源
AMD规范:AMD模块须要用define来明肯定义一个模块,而在Node实现中是隐式包装的
CMD规范:CMD与AMD规范的主要区别在于定义模块和依赖引入的部分
兼容多种模块规范:包装兼容Node、AMD、CMD以及常见的浏览器环境中
第三章 异步IO
伴随着异步I/O的还有事件驱动和单线程
3.1.为何要异步I/O
用户体验:I/O是昂贵的,分布式I/O是更昂贵的
资源分配:利用单线程,远离多线程死锁、状态同步等问题;利用异步I/O,让单线程远离阻塞,以更好的使用CPU
3.2.异步I/O与实现现状
异步I/O在Node中应用最为普遍,可是它并不是Node的原创
异步I/O与非阻塞I/O:操做系统内核对于I/O只有两种方式:阻塞与非阻塞;现存的轮询技术主要有:read,select,poll,epoll,kequeue(read:重复调用来检查I/O的状态来完成完整数据的读取;select:经过对文件描述符上的事件状态来进行判断;poll:采用链表的方式避免数组长度的限制,其次能避免不须要的检查;epoll:Linux下效率最高的I/O事件通知机制;kqueue:与epoll相似,不过仅在FreeBSD系统下存在)
理想的非阻塞异步I/O
现实的异步I/O:在Node中,不管是*nix仍是Windows平台,内部完成I/O任务的另有线程池
3.3.Node的异步I/O
事件循环:Node自身的执行模型:事件循环
观察者:在Node中,事件主要来源于网络请求、文件I/O等;在Windows下,这个循环基于IOCP建立,而在*nix下则基于多线程建立;
请求对象:从JavaScript发起调用到内核执行完I/O操做的过渡过程当中存在的一种中间产物
执行回调:事件循环、观察者、请求对象、I/O线程池这四者共同构成Node异步I/O模型的基本要素;完成异步I/O的过程(Windows下经过IOCP向系统内核发送I/O调用和从内核获取已完成的I/O操做,配以事件循环;Linux下经过epoll实现;FreeBSD下经过kqueue实现;Solaris下经过Event ports实现)
3.4.非I/O的异步API
与I/O无关的异步API:setTimeout()、setInterval()、setImmedate()、process.nextTick()
定时器:实现原理与异步I/O比较相似,只是不须要I/O线程池的参与;问题在于并不是精确的;
process.nextTick():采用定时器须要动用红黑树,建立定时器对象和迭代等操做;定时器中采用红黑树的操做时间复杂度为O(lg(n)),nextTick()的时间复杂度为O(1);
setImmediate():process.nextTick()中的回调函数执行的优先级要高于setImmediate();process.nextTick()属于idle观察者,setImmediate()属于check观察者;
3.5.事件驱动与高性能服务器
Node无需为每个请求建立额外的对应线程
不受线程上下文切换开销的影响
一些知名的基于事件驱动的实现:Ruby的Event Machine;Perl的AnyEvent;Python的Twisted;
第四章 异步编程
Node是首个将异步大规模带到应用层面的平台
4.1.函数式编程
高阶函数:高阶函数是能够把函数做为参数,或是将函数做为返回值的函数
偏函数用法:偏函数用法是指建立一个调用另一个部分参数或变量已经预置的函数的函数的用法;经过指定部分参数来产生一个新的定制函数的形式就是偏函数;
4.2.异步编程的优点与难点
优点:Node的异步模型和V8的高性能
难点:异常处理、函数嵌套、阻塞代码、多线程编程、异步转同步
4.3.异步编程解决方案
事件发布/订阅模式:若是一个事件添加了超过10个侦听器将会获得一条警告,使用emitter.setMaxListeners(0)去掉限制
Promise/Deferred模式:Deferred主要是用于内部,用于维护异步模型的状态;Promise则做用与外部,经过then()方法暴露给外部以添加自定义逻辑;Promise/Deferred模式将业务中不可变的部分封装在了Deferred,将可变的部分交给Promise;
流程控制库:尾触发与Next,目前应用最多的地方是Connect的中间件;async,长期占据NPM依赖榜的前三名,series实现异步的串行执行,parallel实现异步的并行执行,waterfall实现异步调用的依赖处理;step,更轻量;wind,思路彻底不一样的异步编程方案;
4.4.异步并发控制
同步I/O,每一个I/O都是彼此阻塞的,不会出现耗用文件描述符太多的状况
bagpip的解决方案:bagpipe模块的解决思路(经过一个队列来控制并发量;调用发起但未执行的异步调用量小于限定值,从队列中取出执行;若是活跃调用达到限定值,调用暂时存放在队列中;每个异步调用结束时,从队列中取出新的异步调用执行;);拒绝访问;超时控制;
async的解决方案:async中parallelLimit()用于处理异步调用的限制
第五章 内存控制
基于无阻塞、事件驱动创建的Node服务,具备内存消耗低的优势,很是适合处理海量的网络请求
5.1.V8的垃圾回收机制与内存限制
Node与V8
V8的内存限制:只能使用部份内存(64位系统下约为1.4G,32位系统下约为0.7G)
V8的对象分配:V8依然提供选项让咱们使用更多的内存;--max-old-space-size设置老生代内存空间的最大值;--max-new-space-size设置新生代内存空间大小;
V8的垃圾回收机制:垃圾回收策略主要基于分代式垃圾回收机制;在分代的基础上,新生代的对象主要经过Scavenge算法进行垃圾回收;V8在老生代中主要采用Mark-Sweep和Mark-Compact相结合的方式进行垃圾回收;
查看垃圾回收日志:在启动时添加--trace-gc参数;node启动时使用--prof参数,能够获得V8执行时的性能分析数据;提供linux-tick-processor工具用于统计日志信息;
5.2.高效使用内存
做用域:能造成做用域的有函数调用、with以及全局做用域
闭包:实现外部做用域访问内部做用域中变量的方法
没法当即回收的内存有闭包和全局变量引用这两种状况
5.3.内存指标
查看内存使用状况:process.memoryUsage()能够看到Node进程的内存占用状况;os模块的totalmem()和freemem()查看系统的总内存和闲置内存;
堆外内存:不经过V8分配的内存称为堆外内存;利用堆外内存能够突破内存限制的问题;
5.4.内存泄露
形成内存泄露的缘由几个:缓存、队列消费不及时、做用域未释放
慎将内存看成缓存:在Node中任何试图拿内存当缓存的行为都应当被限制
关注队列状态:使任何异步调用的回调都具有可控的响应时间
5.5.内存泄露排查
常见的用于定位Node应用内存泄露的工具:v8-profiler、node-headpdump、node-mtrace、dtrace、node-memwatch
node-headdump
node-memwatch
5.6.大内存应用
Node提供了stream模块用于处理大文件
要当心,即便V8不限制堆内存的大小,物理内存依然有限制
第六章 理解Buffer
6.1.Buffer结构
模块结构:Buffer是一个像Array的对象,但它主要用于操做对象
Buffer对象:
buf[10]
的元素值是一个0到255的随机值
Buffer内存分配:Buffer对象的内存分配不是在V8的堆内存中,而是在Node的C++层面实现内存的申请的;Node以8KB为界限来区分Buffer是大对象仍是小对象;真正的内存是在Node的C++层面提供的,JavaScript层面只是使用它;
6.2.Buffer的转换
目前支持的字符串编码类型:ASCII、UTF-八、UTF-16LE/UCS-二、Base6四、Binary、Hex
字符串转Buffer
Buffer转字符串
Buffer不支持的编程类型:Buffer提供一个isEncoding()函数来判断编码是否支持转换;iconv和iconv-lite两个模块能够支持更多的编码类型转换;
6.3.Buffer的拼接
乱码是如何产生的
setEncode()与string_decoder()
正确拼接Buffer:调用Buffer.concat()方法生成一个合并的Buffer对象
6.4.Buffer与性能
Buffer在文件I/O和网络I/O中运用普遍
Buffer是二进制数据,字符串与Buffer之间存在编码关系
第七章 网络编程
Node提供了net、dgram、http、https这4个模块,分别用于处理TCP、UDP、HTTP、HTTPS
7.1.构建TCP服务
TCP
建立TCP服务器端
TCP服务的事件:TCP套接字是可写可读的Stream对象,能够利用pipe()方法巧妙的实现管道操做
7.2.构建UDP服务
建立UDP套接字
建立UDP服务器端
建立UDP客户端
UDP套接字事件:UDP套接字只是一个EventEmitter的实例,而非Stream的实例
7.3.构建HTTP服务
HTTP
http模块:TCP服务以connection为单位进行服务,HTTP服务以request为单位进行服务
HTTP客户端
7.4.构建WebSocket服务
WebSocket握手
WebSocket的数据传输
7.5.网络服务与安全
Node在网络安全上提供了3个模块,分别为crypto、tls、https
TLS/SSL
TLS服务
HTTPS服务
第八章 构建Web应用
8.1.基础功能
请求方法
路径解析
查询字符串
Cookie:Cookie处理的几步(服务器向客户端发送cookie;浏览器将Cookie保存;以后每次浏览器都会将Cookie发向服务器端)
Session:常见的两种实现方式(基于Cookie来实现用户和数据的映射;经过查询字符串来实现浏览器端和服务器端数据的对应);Connect默认采用connect_uid,Tomcat会采用jsessionid等;
缓存:提升性能,YSlow中提到的几条关于缓存的规则(添加Expires或Cache-Control到报文中;配置ETags;让Ajax可缓存)
Basic认证:经过Base64加密后在网络中传送,有太多的缺点
8.2.数据上传
表单数据:经过报头的Transfer-Encoding或Content-Length便可判断请求中是否带有内容
附件上传:浏览器在遇到multipart/form-data表单提交时,构造的请求报文与普通表单彻底不一样;formidable,基于流式处理解析报文,将接收到的文件写入到系统的临时文件夹中,并返回对应的路径;
数据上传与安全
8.3.路由解析
文件路径型
MVC
RESTful
8.4.中间件
异常处理
中间件与性能
从凌乱的发散状态收敛成很规整的组织方式
8.5.页面渲染
内容响应:不一样的文件类型具备不一样的Mime值;Content-Disposition字段影响的行为是客户端会根据它的值判断是应该将报文数据看成即时浏览的内容,仍是可下载的附件;
视图渲染
模板:实质就是将模板文件和数据经过模板引擎生成最终的HTML代码;造成模板技术4个要素(模板语言;包含模板语言的模板文件;拥有动态数据的数据对象;模板引擎);mustache,弱逻辑的模板;最知名的有EJS、Jade等;
Bigpipe:用于调用限流,解决重数据页面的加载速度问题;解决思路是将页面分割成多个部分,先向用户输出没有数据的布局,将每一个部分逐步输出到前端,再最渲染填充框架,完成整个网页的渲染;
第九章 玩转进程
9.1.服务模型的变迁
石器时代:同步 - 只在一些无并发要求的应用中存在
青铜时代:复制进程 - 要复制较多的数据,启动是较为缓慢的
白银时代:多进程 - 时间将会被耗用在上下文切换中
黄金时代:事件驱动 - 内存耗用的问题著名的C10k问题;单线程避免了没必要要的内存开销和上下文切换开销;
9.2.多进程架构
child_process.fork()复制的都是一个独立的进程,独立而全新的V8实例
启动多个进程只是为了充分将CPU资源利用起来,而不是为了解决并发问题
建立子进程
进程间通讯:JavaScript主线程与UI渲染共用同一个线程;实现进程间通讯的技术有不少,如命名管道、匿名管道、socket、信号量、共享内存、消息队列、Domain Socket等;操做系统的文件描述符是有限的;
句柄传递:句柄是一种能够用来标识资源的引用,它的内部包含了指向对象的文件描述符;文件描述符其实是一个整数值;Node进程之间只有消息传递,不会真正的传递对象;
9.3.集群稳定之路
进程事件
自动重启:建立新工做进程在前,退出异常进程在后
负载均衡:轮叫调度的工做方式是由主进程接受链接,将其依次分发给工做进程
状态共享:解决数据共享最简单、直接的方式就是经过第三方进行数据存储;主动通知;
9.4.Cluster模块
Cluster工做原理:事实上是child_process和net模块的组合应用
Cluster事件
第十章 测试
10.1.单元测试
编写可测试代码的几个原则:单一职责、接口抽象、层次分离;
单元测试主要包含断言、测试框架、测试用例、测试覆盖率、mock、持续继承等,因为Node的特殊性,还会加入异步代码测试和私有方法的测试;
JavaScript的断言规范最先来自于CommonJS的单元测试规范;
单元测试风格主要有TDD(测试驱动开发)和BDD(行为驱动开发)两种;
BDD对测试用例的组织主要采用describe和it进行组织;
TDD对测试用例的组织主要采用suite和test完成;
单元测试覆盖率方便咱们定位没有测试到的代码行;
私有方法的测试(Java一类的语言,私有方法访问能够经过反射的方式实现;巧妙利用闭包的诀窍,在eval()执行时,实现对模块内部局部变量的访问,从而能够将局部变量导出给测试用例调用执行;)
10.2.性能测试
单元测试主要用于检测代码的行为是否符合预期,性能测试的范畴比较普遍,包括负载测试、压力测试和基准测试
基准测试
压力测试:最经常使用的工具是ab、siege、http_load等
基准测试驱动开发
测试数据与业务数据的转换
第十一章 产品化
11.1.项目工程化
目录结构
构建工具:在Web应用中一般会在Makefile文件中编写一些构建任务来帮助提高效率;合并编译、应用打包、运行测试、清理目录、扫描代码等;
编码规范:一种是文档式的约定,一种是代码提交时的强制检查
代码审查
11.2.部署流程
部署环境
部署操做
11.3.性能
几个拆分原则:作专注的事;让擅长的工具作擅长的事情;将模型简化;将风险分离;
动静分离
启动缓存
多进程架构
读写分离:进行数据库的读写分离,将数据库进行主从设计
11.4.日志
访问日志
异常日志:log与info方法都将信息输出给标准输出process.stdout,warn与error方法则将信息输出到标准错误process.stderr;console对象上有个Console属性,它是console对象的构造函数;回调函数中产生的异常,交给全局的uncaughtException事件去捕获;
日志与数据库
分割日志
11.5.监控报警
监控:一种是业务逻辑型的监控,一种是硬件型的监控;主要指标:日志监控、响应时间、进程监控、磁盘监控、内存监控、CPU占用监控、CPU load监控、I/O负载、网络监控、应用状态监控、DNS监控;
报警的实现
监控系统的稳定性
11.6.稳定性
典型的水平扩展方式就是多进程、多机器、多机房
写在后面
pdf书籍、笔记思惟导图、随书代码打包下载地址:
https://pan.baidu.com/s/1OhLjjtfffjX3hv2_Pw7AHQ
(提取码:9m33)
纸质书京东购买地址:
https://u.jd.com/wHmeh4
(推荐购买纸质书来学习)
相关文章
1.
[书籍精读]《深刻浅出Node.js》精读笔记分享
2.
精读《深刻浅出Node.js》
3.
读书笔记: 深刻浅出node.js
4.
[书籍精读]《CSS世界》精读笔记分享
5.
[书籍精读]《React进阶之路》精读笔记分享
6.
[书籍精读]《React Native精解与实战》精读笔记分享
7.
Node.js: 深入浅出Nodejs读书笔记
8.
[书籍精读]《你不知道的JavaScript(下卷)》精读笔记分享
9.
[书籍精读] 《你不知道的JavaScript(上卷)》精读笔记分享
10.
《深刻浅出ORACLE》读书笔记
更多相关文章...
•
RSS 阅读器
-
RSS 教程
•
PHP MySQL 读取数据
-
PHP教程
•
Tomcat学习笔记(史上最全tomcat学习笔记)
•
JDK13 GA发布:5大特性解读
相关标签/搜索
精读
读书分享
读书笔记
深刻浅出Node.js
深刻浅出
原创精读
精读猫说
3000字精读
精读源码
Node.js
JavaScript
Redis教程
PHP教程
MyBatis教程
0
分享到微博
分享到微信
分享到QQ
每日一句
每一个你不满意的现在,都有一个你没有努力的曾经。
最新文章
1.
微软准备淘汰 SHA-1
2.
Windows Server 2019 Update 2010,20H2
3.
Jmeter+Selenium结合使用(完整篇)
4.
windows服务基础
5.
mysql 查看线程及kill线程
6.
DevExpresss LookUpEdit详解
7.
GitLab简单配置SSHKey与计算机建立连接
8.
桶排序(BucketSort)
9.
桶排序(BucketSort)
10.
C++ 桶排序(BucketSort)
本站公众号
欢迎关注本站公众号,获取更多信息
相关文章
1.
[书籍精读]《深刻浅出Node.js》精读笔记分享
2.
精读《深刻浅出Node.js》
3.
读书笔记: 深刻浅出node.js
4.
[书籍精读]《CSS世界》精读笔记分享
5.
[书籍精读]《React进阶之路》精读笔记分享
6.
[书籍精读]《React Native精解与实战》精读笔记分享
7.
Node.js: 深入浅出Nodejs读书笔记
8.
[书籍精读]《你不知道的JavaScript(下卷)》精读笔记分享
9.
[书籍精读] 《你不知道的JavaScript(上卷)》精读笔记分享
10.
《深刻浅出ORACLE》读书笔记
>>更多相关文章<<