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

异步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、在执行宏任务一个宏任务如果发现还有微任务继续执行微任务

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,478评论 5 467
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,825评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,482评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,726评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,633评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,018评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,513评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,168评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,320评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,264评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,288评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,995评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,587评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,667评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,909评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,284评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,862评论 2 339

推荐阅读更多精彩内容