Node.js

Node.js 知识量:9 - 37 - 115

3.3 Node的异步I/O><

事件循环- 3.3.1 -

Node.js的异步I/O基于事件循环模型实现。事件循环模型的核心是事件循环,这是一个循环体,每执行一个循环被称为一个Tick。每个Tick的过程中,事件循环会查看是否有待处理的事件,如果有,就取出并执行相关回调函数。这个过程中涉及到观察者模型,每个事件循环中有一个或多个观察者,判断是否有事件要处理的过程就是向这些观察者询问是否有要处理的事件。

Node.js的事件循环还会利用I/O线程池进行异步I/O操作。当涉及到I/O操作时,Node.js会开一个独立的线程来进行异步I/O操作,操作结束以后将消息压入消息队列。这样,事件循环可以继续处理其他任务,而不会阻塞等待I/O操作完成。

这种异步I/O模型的优势在于能够处理大量并发请求,提高系统的并发处理能力,避免线程阻塞和死锁问题,同时降低系统资源的浪费。然而,它也带来了编程模型上的挑战,需要开发人员熟悉异步编程的技巧和最佳实践,以避免出现一些常见的错误和问题。

观察者- 3.3.2 -

Node.js的异步I/O中,观察者是一种机制,用于判断是否有事件待处理。在每个Tick的过程中,观察者会询问是否有事件要处理。如果存在关联的回调函数,就会执行它们。

观察者模型的作用是将事件进行了分类。在Node.js中,事件主要来源于网络请求、文件I/O等,这些事件对应的观察者有文件I/O观察者、网络I/O观察者等。观察者将事件进行了分类,事件循环则从观察者那里取出事件并处理。

这种机制实现的方式是通过多线程。在Windows下,这个循环基于IOCP创建,而在*nix下则基于多线程创建。Node.js并不是单线程的,只是JavaScript执行在单线程中,I/O线程之间是可以并行的,比如文件I/O与磁盘I/O。

请求对象- 3.3.3 -

在Node.js的异步I/O中,请求对象是一个重要的中间产物。当JavaScript发起调用到内核执行完I/O操作的过渡过程中,存在一种中间产物,即请求对象。所有的状态都保存在这个对象中,包括送入线程池等待执行以及I/O操作完毕后的回调处理。

当请求对象被创建后,执行回调,然后组装好请求对象、送入I/O线程池等待执行,实际上完成了异步I/O的第一部分。当I/O操作完成后,会将获取的结果储存在req->result属性上,然后调用PostQueuedCompletionStatus()通知IOCP,告知当前对象操作已经完成。

在事件循环中,观察者会将事件进行分类。当事件循环检查到有事件待处理时,就会从对应的观察者那里取出事件并处理。

执行回调- 3.3.4 -

在Node.js中,异步I/O操作完成后,会执行相应的回调函数。这个回调函数是在事件循环的下一个Tick中执行的。当I/O操作完成时,会将结果存储在请求对象的属性上,然后通过调用PostQueuedCompletionStatus()函数通知IOCP(I/O Completion Port)操作已经完成。

在事件循环中,观察者会将事件进行分类。当事件循环检查到有事件待处理时,就会从对应的观察者那里取出事件并执行相应的回调函数。这个过程是异步的,不会阻塞事件循环的执行,从而使得Node.js能够处理大量并发请求,提高系统的并发处理能力。

需要注意的是,回调函数的执行是在事件循环的下一个Tick中,而不是在I/O操作完成后立即执行。这是因为Node.js的事件循环是多线程的,I/O操作是在独立的线程中完成的,而回调函数的执行是在主线程中进行的。这种异步和非阻塞的特性使得Node.js能够高效地处理大量并发请求,同时也带来了编程模型上的挑战,需要开发人员熟悉异步编程的技巧和最佳实践。