为何要使用异步I/O?

使用一部I/O是为了提高用户体验,在浏览器端,javascript是单线程的,这意味着在执行javascript代码的时候,会阻塞UI的渲染和响应。而在B/S模型中,网络速度的显示给网页实时体验造成了很大的麻烦,如果说浏览器临时需要获取一个文件,且是通过通过的方式获取的,那么javascript则需等待资源完全从服务器端获取完毕后才会执行后续的工作。在此期间将停止UI渲染,不响应用户的交互行为,这将会很大程度的降低了网页的体验感。而采用异步请求,在下载资源期间,JavaScript和UI的执行都不会处于等待状态,可以继续响应用户的交互行为,给用户一个鲜活的页面

单线程和多线程的区别:

  1. 单线程的串行执行,多线程并行执行。
  2. 单线程的串行编程的思维符合顺序编程的思维,易于逻辑表达,但是由于是串行执行,所以需等前面的代码执行完毕后才会执行后续代码,为此会出现执行阻塞。
  3. 多线程在于创建线程和上下文切换之间的开销大,并且,在复杂的项目中会经常面临锁、状态同步等问题。但是多线程能高效利用cpu的性能。

Node在两者之间给出了它的方案:利用单线程,远离多线程死锁、状态同步等问题;利用异步I/O,让单线程远离阻塞,以更好地使用CPU
**
异步I/O流程示意图
node 异步I_O示意图.png

异步I/O与非阻塞I/O

在介绍node的时候时长会听到异步和非阻塞、回调、事件等名词。其中异步和非阻塞似乎是一回事,从效果而言,异步和非阻塞达到了同样的目的。但是从计算机内核而言,异步/同步,阻塞/非阻塞是两个不同的问题。

操作系统内核对于I/O只有两种方式:阻塞与非阻塞。在调用阻塞I/O时,应用程序需要等待I/O完成才返回结果。

NodeJs的异步I/O

完成整个异步I/O的环节的有事件循环,观察者和请求对象等。

事件循环

node_事件循环.png

观察者

没执行一次循环体的过程我们称之为tick,每个tick判断时间是否需要处理的就需要观察者的概念。每个事件循环的过程中有一个或者多个观察者来判断当前是否有需要处理的事件。

请求对象

从JavaScript发起调用到内核执行完I/O操作的过渡过程中,存在一种中间产物,它就是请求对象。

执行回调

事实上,前面三个步骤属于完成异步I/O的第一部分,而执行回调是第二部分,最终的结果需要通过回调来体现出来。

NodeJs异步I/O的几个关键词:事件循环,观察者,单线程,线程池。这里的单线程和线程池看起来是相互悖论的,但事实上NodeJS中只是javascript是属于单线程的,其Node自身是属于多线程的,只是I/O线程使用的cpu较少,另一个需要重视的观点是,除了用户代码不能并行执行外,所有的I/O都是可以并行执行的。

非I/O的异步Api

setTimeout、setInterval、process.nextTick、setImmediate,这几个api是无关I/O的异步api。

优先级

process.nextTick优先级高于setImmediate,是因为process.nextTick输入idle观察者,setImmediate属于check观察者,在每个事件轮询检查中,idle观察者先于I/O观察者,I/O观察者先于check观察者。

setTimeout和setImmediate执行顺序不确定,根据实际使用情况不同可能会发生不同的情况,他们都同属于check观察者。
image.pngimage.png

事件驱动与高性能服务器

事件驱动的实质:通过主循环加事件触发的方式来驱动程序。

node构建web服务流程图
node构建web服务.png