Node异步模型原理分析

异步模型存在的必要性

1. 用户体验

前端:通过异步的方式使得资源请求期间UI不停顿,不影响用户的交互行为,应用在使用期间依旧可以相应用户的交互行为,给用户提供鲜活的页面。

后端:使得第一个资源的获取不会影响第二个资源,使得多个资源获取的时间消耗变为MAX(M,N,…)。

2. 资源分配

Node在资源分配问题上,利用单线程,远离多线程死锁,状态同步等问题;利用异步I/O,让单线程远离阻塞,以更好的利用CPU。

阻塞,非阻塞,同步,异步,并行,并发

在学习Node.js过程中,阅读到异步I/O的内容时发现自己对异步,同步,阻塞,非阻塞以及并行和并发的概念并不清楚,因此正好在此对上述内容进行学习。

在理解阻塞,非阻塞,异步,同步概念时,首先明确I/O调用是应用程序发起的,I/O执行则是操作系统处理的,而I/O调用的目的是将进程的内部数据迁移到外部即输出,或者将外部数据迁移到进程内部即输入。以一个进程的输入类型的I/O调用为例,它将完成以下工作内容:

  1. 进程向操作系统请求外部数据
  2. 操作系统将外部数据加载到内核缓冲区
  3. 操作系统将数据从内核缓冲区拷贝到进程缓冲区
  4. 进程读取数据继续后面的工作

从上述工作内容来看我们更加理解一个I/O操作,应用程序和操作系统都干了什么。

1. 阻塞&非阻塞

阻塞和非阻塞和是描述应用系统对于操作系统I/O是否处于就绪状态的处理方式。

阻塞的处理方式是在应用进程向操作系统发送I/O调用之后,线程或进程挂起,直到操作系统将数据从内核缓冲区拷贝到进程缓冲区。

而非阻塞的处理方式是在应用程序向操作系统发送I/O调用之后,线程或进程并不挂起,而是去执行其他既定代码,然后通过轮询就绪状态来判断数据是否被拷贝到进程缓冲区。

对于非阻塞的情况,虽然对于CPU的利用率提高了,因为可以执行其他既定代码,但是不断的轮询来判断I/O调用状态的方式则会增添不必要的CPU开销。

2. 同步 & 异步

同步和异步是针对消息通讯机制而言,发起I/O调用后,当前线程或进程是否挂起等待操作系统的I/O执行完成。

同步执行的意思是程序发起I/O调用之后,当前线程或者进程需要等待操作系统完成I/O工作并告知进程已经完成,线程或进程才能继续往下执行其他既定指令。

异步执行的意思是程序发起I/O调用之后,当前线程或进程不需要等待操作希望完成I/O工作,而可以继续往下执行其他既定指令,操作系统完成I/O之后,当前线程或进程会得到操作系统的通知。

以一个读取数据的I/O操作而言,在操作系统将外部数据写入进程缓冲区这个期间,进程或线程挂起等待操作系统I/O执行完成的话,这中I/O执行策略就为同步,如果进程或线程并不挂起而是继续工作,这种I/O执行策略便为异步。

3. 并发 & 并行

并发操作是指当有多个线程在操作时,如果系统只有一个CPU,操作系统只能把CPU运行时间划分成若干个时间段,再将时间段分配给各个线程执行,在一个时间段的快速的切换不同的线程代码运行。

并行操作是指当系统有多个CPU时,可以存在当一个CPU执行一个线程时,另一个CPU可以执行另一个线程,两个线程互不抢占CPU资源,可以同时进行。

阻塞 & 非阻塞与同步 & 异步的区别

同步与阻塞的区别在于同步会发出I/O调用并且在没有得到结果之前,该调用就不会返回,但是此时该线程并不会挂起,而阻塞则是在调用结果返回之前,当前线程会被挂起,只有函数得到结果之后才会返回。

异步与非阻塞的区别在于异步I/O调用之后并不会等待结果,而是等待操作系统回馈通知并执行回调来响应I/O,而非阻塞则会在I/O调用之后立即返回结果而不挂起线程,但是由于I/O返回的结果可能是O_NONBLOCK表示的操作系统I/O操作并未完成,于是非阻塞常常伴随着轮询的方式来确认操作系统I/O操作是否完成。

现代常见的I/O模型

1. I/O多路复用

I/O多路复用是为了使用一个线程对多个I/O进行管理,提高服务器的吞吐量,多路复用的实现有多种方式:select,poll,epoll等如果想对I/O多路复用有更加深入的理解,请看文章漫谈五种IO模型(主讲IO多路复用),此处附上I/O多路复用的示意图(以select为例):

I/O多路复用

2. 信号驱动I/O

信号驱动I/O是通过向系统内核注册信号处理函数,然后当内核成功将数据拷贝到内核缓冲区之后,内核发送SIGIO信号通知应用程序调用I/O操作函数处理数据。

信号驱动I/O

3. 异步I/O

异步I/O相比于信号驱动I/O,内核不再发送SIGIO信号,而是内核直接将数据拷贝到进程缓冲区,然后直接返回让应用程序直接调用。

异步I/O

Node的单线程异步模型

上面我们描述了常见的I/O模型,其中就看到存在异步I/O模型,那么node的异步模型与上述I/O模型存在怎样的区别呢?

上述I/O模型是描述的单线程环境下,内核与应用线程之间的I/O模型,而Node则使用多线程的方式来解决单线程I/O阻塞的问题。Node使用一个线程进行计算处理,执行应用逻辑,然后使用线程池进行阻塞I/O或者非阻塞I/O加轮询技术来完成数据获取,从而模拟异步I/O,示意图如下:

Node异步I/O

除了上述Node的异步I/O模型之外,整个异步I/O环节还包含事件循环,观察者,请求对象以及执行回调

1. 事件循环

Node的事件循环机制类似于浏览器的事件循环,对于前端开发者来说此处的循环机制非常容易理解,首先Node会创建一个类似于while(true)循环,没执行一次循环体的过程称为一个Tick,每个Tick就是查询事件队列是否有事件需要执行,如果有就取出事件及相关的回调函数并执行,此次Tick完成后就进入下一个循环,如果不再有事件处理就退出进程。

Node事件循环

2. 观察者

Node使用那个观察者何来在每个Tick中判断是否有事件需要处理,每个事件循环中有一个或多个观察者,而判断是否有事件要处理的过程就是向这些观察者询问是否有要处理的事件。

过程就如同饭馆的厨房,厨房一轮一轮的制作菜肴,但是要具体制作哪些菜肴取决于收银台收到的客人的下单。厨房没每做完一轮菜肴就去问收银小妹,接下来有没有要做的菜,如果没有的话,就打烊下班。整个过程中收银小妹就是观察者,而客人点单就是关联的回调函数。

3. 请求对象

由于Node异步I/O中回调函数不是由开发者调用,因此在发出调用到回调函数执行的过程中会生成一种中间产物,叫做请求对象

I/O调用的过程为JavaScript调用Node核心模块,核心模块调用C++内建模块,内建模块通过libuv进行系统调用。在libuv调用系统模块过程中会创建一个FSReqWrap请求对象,从JavaScript层传入的参数和当前方法都会被封装在这个请求对象中,回调函数则设置在请求对象的oncomplete_sym属性上。然后在Windows下则会调用QueueUserWorkItem()方法将FSReqWrap对象推入线程池中等待。至此JavaScript调用立即返回,由JavaScript层面发起的异步调用第一阶段就此结束。

请求对象是异步I/O过程的中间产物,所有的状态都保存在这个对象中,包括送入线程池等待执行以及I/O操作完毕后的回调函数。

4. 执行回调

在I/O操作执行完毕后,会将结果存储在req->result属性上,然后调用PostQueuedCompletionStatus()方法通知IOCP,告知当前对象操作已完成。PostQueuedCompletionStatus()作用是像IOCP提交执行状态,并将线程归还线程池。

事件循环的观察者会调用IOCP相关的GetQueuedCompletionStatus()方法检查线程池中是否有执行完的请求,如果存在会将请求加入到I/O观察者的队列中,然后将其当做事件处理。I/O观察者回调函数的行未就是取出请求对象result属性作为参数,取出oncomplete_sym属性作为方法,然后调用执行,以此达到调用JavaScript中传入的回调函数的目的。

至此,整个异步I/O流程完全结束。

总结

事件循环,观察者,请求对象,I/O线程池这四者共同构成了Node异步I/O模型的基本要素。

Windows下主要通过IOCP来向系统内核发送I/O调用和从内核获取已完成的I/O操作,配以事件循环,以此完成异步I/O的过程。在Linux下通过epoll实现这个过程,FreeBSD下通过kqueue实现,Solaris下通过Event ports实现,不同的是线程池在Windows下由内核(IOCP)直接提供. *nix系列下由libuv自行实现。

参考文档

  1. 朴灵的《深入浅处node.js》
  2. Linux下五种I/O模型详解(阻塞IO、非阻塞IO、IO复用、信号驱动、异步IO)
  3. 漫谈五种IO模型(主讲IO多路复用)

本文主要是对学习node过程中知识的记录,行文仓促,如有错误还望斧正

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值