1,什么是Node?
首先介绍下什么是node
1,单线程
node保持了javascript在浏览器中单线程的特点,Node中,Javascript与其余线程无法共享任何状态。
单线程好处:不用处处在意状态的同步,没有死锁,也没有线程上下文切换带来的性能开销。
单线程坏处:无法利用多核cpu,错误会引起整个应用退出,应用的健壮性值得考验,大量计算占用CPU无法继续调用异步I/O。
(注意:针对这些单线程坏处,node都有解决方案,这里不谈)
2,跨平台 (基于libuv)
兼容Windows和*nix平台主要得益于Node在架构层的的改动,它在操作系统与Node与操作系统之间构建了一层平台层框架,即libuv。目前,libuv已经是许多系统实现跨平台的基础组件。
通过良好的架构,Node的第三方C++模块也可以借助libuv实现跨平台,目前,除了没有保持更新的C++模块外,大部分C++模块都能实现跨平台的兼容。
3,模块机制
Java有类文件,Python有import机制,Ruby有require,PHP有include和require。浏览器端Javascript是没有标准的模块机制的,只能通过<script>标签引入代码显得杂乱无章,
语言本身没有组织和约束能力,开发者不得不通过命名空间等方式人为约束代码。-》AMD,CMD。
CommonJS规范:希望javascript能够在任何地方运行。
CommonJS的规范提出主要是为了弥补当前Javascript没有标准的缺陷,以达到像Python,Ruby和Java具备开发大型应用的基础能力,而不是停留在小脚本程序的阶段.NodeJS是这种规范的实现
CommonJS规范涵盖了模块,二进制,Buffer,字符集编码,I/O流,进程环境,文件系统,套接字,单元测试,Web服务网管接口,包管理等。
CommonJS有很多实现,其中不乏很多大名鼎鼎的项目,比如 说:Apache的CouchDB和node.js等。但这些项目大 部分只实现了CommonJS的部分规范。具体的项目和实现部分参见官方网站的说 明:http://commonjs.org/impl/
Node模块实现
4,应用场景
I/O密集型
是否不擅长cpu密集型?
2, 异步的原理。
但凡这种「既是单线程又是异步」的语言有一个共同特点:它们是 event-driven 的。驱动它们的 event 来自一个异构的平台。
异步调用(封装参数)-》线程池(iocp)-》事件循环(监听者-》执行回调函数)
阻塞I/O与非阻塞I/O:操作系统内核对于I/O只有两种方式:阻塞与非阻塞。
[注]:操作系统对计算机进行了抽象,将所有输入输出设备抽象为文件,内核在进行文件I/O操作时,通过文件描述符进行管理,而文件描述符类似于应用程序与系统内核
之间的凭证。应用程序如果需要进行I/O调用,需要先打开文件描述符,然后根据文件描述符去实现文件的数据读写。此处阻塞/O与非阻塞I/O的区别在于阻塞I/O完成整个获取
数据的过程,而非阻塞I/O则不带数据直接返回,要获取数据,还需要通过文件描述符再次读取。
阻塞I/O的一个特点是一定要等待系统内核层面完成所有数据操作后,调用才结束。以读取磁盘文件为例:系统内核在完成磁盘寻道,读取数据,复制数据到内存中之后这个调
用才结束。如图:
阻塞I/O造成了CPU等待,浪费了等待时间,CPU的处理能力不能得到充分利用。为了提高性能,内核提高了非阻塞I/O。非阻塞I/O跟阻塞I/O的区别为调用之后立即返回,如图:
非阻塞I/O返回之后,CPU的时间片可以用来处理其他事务,此时性能提升是明显的。
但非阻塞I/O也存在一些问题。由于完整的I/O没有完成,立即返回并不是业务层期望的数据,而仅仅是打当前调用的状态,为了获取完整的数据,应用层序需要重复调用I/O操作来
确认是否完成。这种重复的调用判断操作是否完成的技术叫做轮询。[注]:任意技术都并非是完美的,阻塞I/O造成CPU等待浪费,非阻塞I/O带来的麻烦却是需要轮询取确认是否
完全完成数据获取,他会让CPU处理状态判断,是对CPU的资源浪费。
轮询技术是有演进的,以减少I/O状态判断的CPU损耗。从read->select->poll->epoll。具体情况这里不谈。
理想的非阻塞异步I/O:我们期望的完美的异步I/O应该是应用程序发起非阻塞调用,无需通过遍历或者事件唤醒灯方式轮询,可以直接处理下一个任务,只需要在I/O完成后通过信号
或者回调函数讲数据传递给应用程序即可,如下图:
幸运的是,在Linux中存在这中方式,它提供一个异步的I/O方式(AIO)就是通过信号或回调来传递数据的。但不幸的是,只有Linux中有,并且AIO只支持内核I/O中的O_DIRECT方法
读取,并且无法利用系统缓存。
现实的异步I/O
现实肯定比理想要骨感一些,但是要达成异步I/O的目标,也并非难事。前面我们讲场景都限定在单线程的状态下,多线程的方式就是另一番风景了。通过让部分线程进行阻塞I/O
或者非阻塞I/O加轮询技术来完成数据获取,让一个线程进行计算,通过线程之间的同学讲I/O得到数据进行传递,这就轻松实现了异步I/O(尽管是模拟的)。
[注]:这里的I/O不仅仅只局限于磁盘读写*nix将计算机抽象了一番,磁盘文件,硬件,套接字等几乎所有计算机资源都被抽象成了文件,因此这里的描述的阻塞和非阻塞的情况
同样适合套接字等。
[注]:我们虽然时常提到Node是单线程的,这里的单线程仅仅指的是Javascript执行在但形成中罢了。在Node中,无论是什么平台,内部完成的I/O任务都是另有线程池的。
3,为什么要用异步?
4,常用的异步库
Deferred是前端解决异步操作的一种编程范式,后来出现的Promise规范更是让其普适性大大提高。不过Promise规范也存在分岐。现在最流行的是Promise/A+规范。
4,1 Promise
PolyFill
1,jakearchibald/es6-promise
一个兼容 ES6 Promises 的Polyfill类库。 它基于 RSVP.js 这个兼容 Promises/A+ 的类库, 它只是 RSVP.js 的一个子集,只实现了Promises 规定的 API。
2,yahoo/ypromise
这是一个独立版本的 YUI 的 Promise Polyfill,具有和 ES6 Promises 的兼容性。
3,getify/native-promise-only
Promise
扩展类库
1,kriskowal/q
使用过Node.js的人可能会知道Q
模块,Q
实现了Promises
和 Deferreds
等规范,在Node.js中环境或浏览器环境中使用。
2,then/promise
一个Promise/A+
简单实现模块,除实现then
方法外,还扩展一些标准外的方法,在Node.js中环境或浏览器环境中使用。
3,petkaantonov/bluebird
bluebird
类库除了兼容Promise
规范之外,还对Promise
对象进行了一定的扩展,如:取消promise对象的运行等。另外,还在运行效率上进行了一定的优化。bluebird
也是一 个npm
模块,可以在Node.js中环境或浏览器环境中使用。
《深入浅出node》作者朴灵写的
EventProxy参考:https://github.com/JacksonTian/eventproxy Wind.js https://github.com/JeffreyZhao/wind http://blog.fens.me/nodejs-async-windjs/