Node-1.高性能服务器

寵の児 提交于 2020-03-10 03:30:46

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可以构建高性能服务器。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!