深入理解Node.js事件驱动模型

1,662 阅读3分钟

本文主要讨论如下问题:

1.Node.js事件驱动模型分析

2.Node.js是如何处理高并发请求的

3.Node.js的缺点介绍

首先对Node.js做个简单介绍,Node.js是一个基于事件驱动、非阻塞式的I/O模型来实现的服务端JavaScript运行环境,是基于Google的V8引擎来实现的单线程、高性能运行在服务端的JavaScript语言。

1、Node.js事件驱动模型分析

image.png

理解了上面这张图,你就理解了Node.js的事件驱动模型。从上图可以看出如下几部分:

  • Application应用层,即JavaScript 交互层,常见的就是 Node.js 的模块,比如 http,fs等
  • V8这一层是V8引擎层,这一层的主要作用是解析JavaScript,同时和应用层和NodeApi层交互
  • NodeApi为上层模块提供系统调用,和操作系统进行交互 。
  • Libuv是跨平台的底层封装,实现了线程池、事件循环、文件操作等,是 Node.js 实现异步的核心 。

在Libuv层维护了一个Event Queue的事件队列,当有请求过来时,经过Node.js的应用层和NodeApi层将请求作为一个事件放到Event Queue事件队列中,并设置回调事件函数,然后继续接受新的请求。

在Libuv层的Event Loop事件循环不断读Event Queue中的事件,在读取事件的过程中如果遇到非阻塞事件,会自已处理,并且在处理完后调用回调函数向上一层返回结果;对于阻塞事件,会委托给后台线程池来处理,当这些阻塞操作完成后,执行结果与提供的回调函数一起再被放入事件队列中。当Event Loop再次读到这个事件时,会再次执行被放到队列中的事件回调函数,最后将结果返回给上一层。具体流程可以参考下图:

image.png

2、Node.js是如何处理高并发请求的

如果你理解了上一个问题,这个问题就很好理解了。如果要总结一下那就是异步非阻塞的编程思想。当遇到比较耗时的操作时,采用异步和非阻塞的方式进入事件队列,不影响后面请求的执行。事件循环会读取到这个耗时请求,交给线程池来处理。当这些耗时的操作处理完后会再次进入事件队列,通过事件循环和回调来将请求结果返回给上一层应用,最后返回给客户端。通过以上方式减少了高并发时的等待,从而可以从容应对高并发。

3、Node.js的缺点介绍

通过以上介绍我们知道了Node.js的事件驱动模型,下面我们将介绍一下Node.js的不足。

Node.js的最大不足就是单线程,同一时间只能服务一个请求。而现在服务器大多数都是多核CPU,这就造成了CPU的利用率非常低,造成资源的浪费。

Node.js的主线程Event Loop在处理事件队列中的事件时都是按照事件队列的顺序执行的,在其中一个任务没有完成之前,其他的回调、监听器等函数都得不到运行的机会,因为被阻塞的Event Loop没有机会处理它们。如果出现这种情况,程序执行就会变慢。