第三章、异步I/O
1. 使用异步I/O的原因:改善用户体验、优化资源分配。Node利用单线程,避免了多线程死锁、状态同步等问题;利用异步I/O,让单线程远离阻塞,以更好的使用CPU。
2、为了弥补单线程无法利用多核CPU的缺点,Node提供了childprocess(类似于HTML5中的web workers),该进程可以通过工作进程高效地利用CPU和I/O。
3、 异步/同步、阻塞/非阻塞。阻塞I/O的一个特点是调用之后一定要等到系统内核完成所有操作之后才返回,造成CPU等待I/O完成,浪费了资源。非阻塞I/O则不带数据直接返回,要获取数据,还需要通过文件描述符再次获取。
TODO 网络I/O模型
4、常见的I/O模型: read、select、poll、epoll、kqueue。Windows下的IOCP
5、Node的异步I/O涉及事件循环、观察者和请求对象等概念。
事件循环:进程启动时,Node创建的一个类似while(true)的循环,每个循环过程成为一个tick,每个tick的过程都会查看是否有事件等待处理,如果有,就取出时间以及事件相关的回调函数,并执行之。然后进入下个循环。如果不再有事件处理,就退出进程。
观察者:在每个事件循环中有一个或者多个观察者,判断是否有事件要处理的过程就是向这些观察者询问是否有需要处理的事件。事件循环是一个典型的生产者/消费者模式。异步I/O、网络请求时事件的生产者,源源不断的为Node提供不同类型的事件,事件被传递到观察者那里,事件循环则从观察者那里取出事件并处理。
请求对象:Node中,Javascript层面的代码通过调用c++核心模块进行下层的操作。从Javascript调用Node的核心模块,核心模块调用c++内建模块,内建模块通过libuv进行系统调用,这是Node中经典的调用方式。请求对象是异步I/O过程中的重要中间产物,所有的状态都保存在这个对象中,包括送入线程池等待执行以及I/O操作完毕后的回调处理。
6、非I/O的异步API:
SetTimeout、setInterval、setImmediate、process.nextTick
TODO: 深入理解js的定时机制
SetTimeout和setInterval创建的定时器会插入到定时器观察者内部的一个红黑树中。每个Tick执行时,都会从红黑树中迭代取出定时器对象,检查是否超过定时时间,如果超过,就形成一个事件、它的回调函数会立即执行。
7、异步I/O并不仅仅应用在文件操作中,对于网络套接字的处理,Node也应用了异步I/O,网络套接字上监听到的请求都会形成事件交给观察者,事件循环会不停的处理这些网络I/O事件。
8、典型的服务器并发模型:
(1)、同步式:一次只处理一个请求,其余请求处于等待状态(最古老的方式)
(2)、多进程:为每个请求启动一个进程,用于处理多个请求。
(3)、多线程:为每个请求启动一个线程来处理
TODO: nginx和apache的并发模型,nginx的事件驱动模型
Node采用事件驱动的方式处理请求,无需为每一个请求创建额外的线程,可以省掉创建线程和销毁线程的开销。
9、几个不错的参考资料:
https://cnodejs.org/ cnodejs:nodejs中文社区
https://nodejs.org/ nodejs社区
https://nodejs.org/api/ API参考文档