美文网首页
深入浅出nodejs(异步I/O)

深入浅出nodejs(异步I/O)

作者: fangPeng__ | 来源:发表于2019-03-17 16:15 被阅读0次

异步IO实现现状

  • I/O的阻塞与非阻塞:IO对于操作系统内核而言,只有阻塞与非阻塞两种方式。阻塞模式的I/O会造成应用程序等待,直到I/O完成,会造成CPU等待IO,浪费等待时间,CPU的处理能力不能充分利用。同时操作系统也支持将I/O操作设置为非阻塞模式,这时应用程序的调用将可能在没有拿到真正数据时就立即返回了,为此应用程序需要多次调用才能确认I/O操作完全完成,但由于IO并没有完成,立即返回的并不是业务层期望的数据,仅仅是当前调用的状态。
  • I/O的同步与异步:I/O的同步与异步出现在应用程序中。如果做阻塞I/O调用,应用程序等待调用的完成的过程就是一种同步状况。相反,I/O为非阻塞模式时,应用程序则是异步的。

堵塞I/O造成CPU的等待浪费,非堵塞I/O是需要轮询去确认是否完成数据获取,让CPU处理状态判断,这是对cpu资源的浪费。

轮询的演进,以减少I/O状态判断的CPU损耗

  • read:性能最低的一种,需要重复调用来检查I/O状态,cpu一直耗在等待上
  • select: 在read基础上的改进方案,通过对文件描述符上的事件状态进行判断(一次只能检查1024文件描述符)
  • poll: 较select有所改进,采用链表的方式避免数据长度的限制,其次它能避免不必要的检查。但在文件描述符过多时,性能十分低下。
    epoll: 该方案是Liunx 下效率最高的I/O事件通知机制,在进入轮询的时候如果没有检查到I/O事件,将会休眠,直到事件将它唤醒。它是真实利用来事件通知、执行回调的方式,而不是遍历查询,所以不会浪费CUP。但是休眠期间CPU是闲置的,对于当前线程而言利用率不够。
    轮询技术满足来非堵塞I/O确保获取完整数据的需求,但是对于应用程序而言,依然是一种同步,因为应用程序依然需要等待I/O完全返回,依旧花费很多时间等待。CPU要么用于遍历文件描述符,要么处于休眠等待事件发生

理想的非堵塞异步I/O

应用程序发起非堵塞调用,无需遍历或者唤醒,可以直接处理下一个任务,只需要在I/O完成后通过信号或者回掉将数据传递给应用程序

现实中的异步I/O

线程池模拟异步I/O。

WechatIMG3.jpeg
通过让部分线程用轮询技术进行数据的获取,让一个线程进行计算处理,通过线程间的通信将I/O得到的数据进行传递
node的上一层libuv 即用此实现,并进行来平台的兼容

node 的异步I/O

事件循环

进程启动时,Node会创建一个类似while(true)的循环,判断是否有事件需要处理,若有,取出事件并执行回调函数。


WechatIMG4111.jpeg

观察者

在Tick 的过程中,引入观察者的概念,用来判断是否有事件需要处理。一个事件循环中有一个或多个观察者,一个观察者里可能有多个事件

请求对象

WechatIMG5.jpeg

从javaScript 调用node的核心模块,核心模块调用C++的内建模块,内建模块通过libux 进行系统调用,这是node的经典调用方式。
javaScript 传入的参数和当前的方法都会被封装到这个请求对象中去,回调函数被设置到这个对象的oncomplete_sym属性上

执行回调

组装好请求对象、送入I/O线程池等待执行完成异步I/O的第一部分,回调通知是第二部分,线程池中的I/O操作调用完毕之后,会把结果存储在req->result属性上,然后调用PostQueuedCompletionStatus()通知IOCP,告知当前对象操作已经完成,
PostQueuedCompletionStatus()方法的作用是向IOCP提交执行状态,并将线程池归还线程,通过这个方法提交状态可GetQueuedCompletionStatus提取,这个工程中动用了事件循环的I/O观察者,每次Tick的执行中,都会调用IOCP相关的GetQueuedCompletionStatus方法检查线程池中是否有执行完毕的请求,如果有就将请求对象加入I/O观察者队列中,然后当作事件处理,I/O观察者调用回调函数就是取出请求对象的result属性作为参数,取出oncomplete_sym作为方法,然后执行。


WechatIMG6.jpeg

非I/O的异步API

setTimeout()、setInterval()、setImmediate()、process.nextTick()

它们与异步I/O相似,只不过不需要I/O线程池的参与,每次Tick时。会检查是否满足条件,如果满足就形成一个事件,所有事件都会被事件循环按顺序排队处理,直到所有队列为空。

在nodejs中有多个观察者队列,不同类型的事件在它们自己的队列中排队。在处理一个类型的队列之后,在进入到下一个队列之前,事件循环将处理两个中间队列(宏任务队列和微任务队列),直到中间队列为空。

  • 宏任务 setTimeout()、setInterval()、setImmediate()
    优先级:主代码块 > setImmediate > setTimeout / setInterval
  • 微任务 process.nextTick(),promise.then()
    优先级:process.nextTick > Promise
执行顺序

1、先执行微任务队列,并且一次执行完。
2、在执行宏任务一个宏任务如果发现还有微任务继续执行微任务

相关文章

  • 深入浅出nodejs(异步I/O)

    异步IO实现现状 I/O的阻塞与非阻塞:IO对于操作系统内核而言,只有阻塞与非阻塞两种方式。阻塞模式的I/O会造成...

  • 多线程同步I/O与单线程异步I/O(笔记)

    学习nodejs时,看到它的简介说它是采用异步I/O与事件驱动的架构设计,搜了很多关于同步I/O和异步I/O的资料...

  • Node学习目录

    深入浅出nodejs学习笔记 章节内容第一章node简介第二章模块机制第三章异步I/O第四章异步编程第五章内存控制...

  • nodejs笔记-异步I/O

    1.为什么要使用异步I/O 1.1 用户体验 浏览器中的Javascripts是在单线程上执行的,并且和UI渲染公...

  • 2 Nodejs02 API

    1、Nodejs基本认知 ->高并发(异步I/O、非阻塞式I/O)、高性能(chrome v8引擎)、事件驱动单线...

  • <<深入浅出Node>>学习笔记

    阅读朴灵大神的深入浅出Node,梳理下阅读笔记,记录之模块机制异步I/O

  • NIO-异步IO

    异步I/O Github Demo 连网是学习异步 I/O 的很好基础,而异步 I/O 对于在 Java 语言中执...

  • Lesson03

    异步式I/O与事件紧密结合 同步式I/O与异步式I/O的区别

  • 异步I/O的难点

    其实异步的I/O的难点与不适,在NodeJs,甚至JavaScript中有这很具体的体现。 难点1:异常处理难。 ...

  • Node 简介

    Node的特点 Node的特点有:异步I/O、事件与回调函数、单线程、跨平台 异步I/O 在Node中,异步I/O...

网友评论

      本文标题:深入浅出nodejs(异步I/O)

      本文链接:https://www.haomeiwen.com/subject/kqcumqtx.html