异步早就存在于操作系统的底层,通过信号量、消息等方式应用。
然而高级编程语言中异步并不多见。
事件驱动、异步I/O设计理念的另一个产品:Nginx 纯C编写
区别:
Nginx具备面向客户端管理连接的能力,受限于同步方式编程语言;
Node是全方位的。
3.1 为什么要异步I/O
3.1.1用户体验
3.1.2资源分配
单线程串行依次执行
优点:顺序执行任务的方式符合编程人员按顺序思考的思维方式
缺点:任意一个略慢的任务都会导致后续执行代码被阻塞
多线程并行完成
优点:在多核CPU能够有效提升CPU的利用率
缺点:①创建线程和执行期线程上下文切换的开销较大
②经常面临锁、状态同步等问题
Node的解决方案:
①使用单线程,远离多线程死锁、状态同步等问题;
②利用异步I/O,让单线程远离阻塞,以更好地使用CPU
③使用类似前端浏览器中WebWorkers的子进程利用多核CPU
3.2 异步I/O实现现状
3.2.1异步I/O与非阻塞I/O
轮询技术:
①read
②select
③poll
④epoll
⑤kqueue
方案实现方式与epoll类似,不过仅在FreeBSD系统下存在
3.2.2理想的非阻塞异步I/O
只有linux下有,并无法利用系统缓存
3.2.3现实的异步I/O
多线程下轻松实现异步I/O
强调:
①这里的I/O包括磁盘文件、硬件、套接字等几乎所有的计算机资源
②node的单线程仅仅只是指JavaScript执行在单线程中,内部完成I/O任务另有线程池。
3.3node的异步I/O
3.3.1事件循环
3.3.2观察者
node中观察者有文件I/O观察者、网络I/O观察者等。
windows下这个循环基于IOCP创建,而在linux下基于多线程创建。
3.3.3请求对象
创建一个FSReqWrap请求对象,从JavaScript层传入的参数和当前方法都被封装在这个请求对象中。回调函数被设置在这个对象的oncomplete_sym属性上。
Windows中,调用QueueUserWorkItem()方法将其推入线程池中等待执行
QueueUserWorkItem(&uv_fs_thread_proc,\
req, \
WT_EXECUTEDEFAULT)
第一个参数:将要执行的方法的引用,调用相应的底层函数
第二个参数:方法运行时所需要的参数
第三个参数:执行的标志
3.3.4执行回调
Node异步I/O模型的基本要素:
①事件循环②观察者③请求对象④I/O线程池
windows下内核(IOCP)直接提供线程池,linux下由libuv自行实现。
3.3.5小结
除了用户代码无法并行执行外,所有的I/O是可以并行起来的。
3.4 非I/O的异步API
setTimeout()、setInterval()、setImmediate()、process.nextTick()
3.4.1定时器
3.4.2process.nextTick()
更轻量的立即执行调用代码
3.4.3setImmediate()
Process.nextTick()的回调函数保存在一个数组中,
setImmediate()的结果保存在链表中。
Process.nextIck()每轮循环中将数组中的回调函数全部执行完
setImmediate()每轮循环执行链表中的一个回调函数。
3.5事件驱动与高性能服务器
3.6 总结
Node依靠一套完善的高性能异步I/O框架打破了JavaScript在服务器端止步不前的局面