Node——高性能服务器
浏览器中JavaScript在单线程上执行,而且与UI渲染共享同一个线程,所以JavaScript在执行的时候UI渲染和响应是处于停滞状态的。如果网页获取资源时同步获取,那么会阻塞页面的其他操作,不能响应用户的交互行为,影响用户体验。
那Nodejs怎么解决性能上的问题?基于事件的非阻塞异步I/O !
通过事件驱动节省为每个请求创建额外线程和切换线程的开销,使得服务器可以在大量连接的情况下也有条不紊地处理请求,非阻塞的设置让它可以更好地提升响应吞吐,使Nodejs构建了一套完善的高性能异步I/O框架。
异步I/O与非阻塞I/O
说到Node的经常会听到异步、非阻塞、回调、事件这些词语。实际上,异步和非阻塞都达到了并行I/O的目的,但是从计算机内核I/O而言,异步/同步和阻塞/非阻塞实际上是两回事。
操作系统内核对于I/O只有两种方式:阻塞与非阻塞。
阻塞I/O: 系统内核调用后一定要等系统内核层面完成所有操作后,调用才结束。
阻塞I/O造成CPU等待I/O,浪费等待时间,CPU的处理能力不能得到充分利用。如读取磁盘上的文件,系统内核在完成磁盘寻道、读取数据、复制数据到内存后,这个调用才结束。
非阻塞I/O: 系统内核调用后会立即返回,可以使CPU得到重分利用,提高性能。
Nodejs的异步I/O
对于Node.js来说,利用单线程,远离多线程死锁、状态同步等问题;利用异步I/O, 让单线程远离阻塞,可以更好利用CPU。
JavaScript发起调用到内核执行完I/O操作的过渡过程中,存在一种中间产物,它叫做请求对象。请求对象 是异步I/O过程中的重要产物,所有的状态都保存在这个对象中,包括送入线程池等到执行以及I/O操作完毕后的回调处理。
异步I/O
第一步:异步调用
JavaScript发起调用后,会将参数和回调方法组装到请求对象上,由Node的核心模块再去进行底层调用,送入I/O线程池等待执行,而JavaScript返回继续往下执行代码,这里完成了异步调用的第一阶段;
第二步:执行回调
线程池中的I/O操作调用完毕后会将结果存储在请求对象中,然后将请求对象加入I/O观察者的队列中,作为事件处理;I/O观察者会取出请求对象中的回调函数及结果执行,从而达到调用JavaScript中传入回调函数的目的。
这就是整个异步I/O的流程。
注意:JavaScript是单线程的,但是Node自身是多线程的,执行用户代码是一个线程,其他线程可以用于处理I/O等其他事务。
事件循环机制
JavaScript是单线程且非阻塞的脚本语言。JavaScript代码在执行的时候只有一个主线程来处理所有任务。当遇到异步代码的时候主线程不会阻塞停下来等待事件的回调执行,而是会按照事件循环机制执行。
什么是事件循环机制?
当JavaScript执行处理到异步代码的时候,会将事件加入事件队列中,等待当前执行栈的任务执行完毕后,主线程再去事件队列查看是否有任务。
异步任务有两种类型:微任务(microtask)和宏任务(macrotask)。不同类型的任务会放到不同的队列中。
当执行栈中所有任务执行完毕后,会先去检查微任务队列中是否有事件存在,如果有会依次执行微任务队列中的回调,直到为空;然后再去宏任务队列中取出一个事件,把对应的回调放入执行栈中执行完毕后,检查微任务列表中是否有事件存在,这样无限循环执行事件,就叫做事件循环。
事件循环是异步实现的核心。
Node与其他服务器比较
与其他Web服务器相比,
- 比如Apache,采用的每线程/每请求,为请求创建额外的对应线程,创建线程和销毁线程需要额外的开销,同时操作系统需要调度任务切为线程换上下文,代价比较高;
- Ruby的Event Machine,Python的Twisted,虽然采用了事件驱动,但是因为同步I/O的存在,导致在事件循环中一旦I/O阻塞,会导致其余的I/O无法立即执行,性能急剧下降,无法及时处理其他请求;
- 对于Nginx也摒弃了多线程,采用了与Node相同的事件驱动,性能不错,可以用于反向代理或负载均衡,不过Node处理具体业务能力更强,场景更广,性能也不错;
所以,Node使用异步I/O使I/O操作与CUP操作分离,而I/O操作的底层实现采用事件驱动,构建了完善的高性能异步I/O框架,使得Node可以构建高性能服务器。
来源:CSDN
作者:想要成为程序媛的DUDUfine
链接:https://blog.csdn.net/DUDUfine/article/details/104756702