记录了Node.js的组成与特点。

一、Node.js简介

Node.js是开源跨平台的JavaScript运行环境。基于chrome V8引擎、Libuv库及第三方模块实现的单线程应用。

其组成架构图如下图: 组成架构

整体分为3个部分:

  1. Node.js 标准库:
    这部分由 JavaScript 编写的 Node.js 核心模块,供使用者直接引用,位于源码的lib目录。
  2. Node Bingdings:
    Node.js 程序的 main 函数入口,对底层模块进行封装为标准库提供C++类接口,是 JavaScript 与底层 C/C++ 沟通的桥梁,由 C++ 编写,位于源码的 src 目录。
  3. 最底层:
    1. V8 引擎: 用于解析、执行 JavaScrpit 代码的运行环境。
    2. libuv: 提供底层I/O操作接口,包括文件异步I/O线程池和网络的I/O操作,是整个异步I/O实现的核心,由 C/C++ 编写,位于源码的 deps 目录。
    3. 其他 C/C++ 组件和库:如 c-ares、http_parser 等,这些依赖提供了对系统底层功能的访问。

二、特点

1. 单线程

传统的 web 服务模型中,大多都使用多线程来解决并发问题。会为每个客户端连接创建一个新的线程,每个线程大约需要消耗2MB内存。理论上,一个8GB内存的服务器可以同时连接最大的用户数为4000个左右。Node.js只有一个主线程,当用户连接时会触发一个内部事件,通过非阻塞I/O、事件驱动机制,实现Node.js程序的并行,一个8GB内存的服务器,可以同时处理4万用户的连接。

优点:操作系统不再有线程创建、销毁的开销。

缺点:一个代码某个环节崩溃会导致整个系统的崩溃。

2. 非阻塞I/O

Node.js 采用了非阻塞I/O。例如,像是执行了访问数据库的代码后,将立即执行后面的代码,将数据库返回结果交由回调函数处理,从而提高了程序执行效率。当某个I/O执行完成后,将以事件的形式通知执行I/O操作的线程,线程执行这个事件的回调函数,为了处理异步I/O,线程必须有事件循环,不断的检查有没有未处理的事件,依次处理。

相反,阻塞模式下,一个线程只能处理一项任务,想要提高吞吐量必须通过多线程的方式。

3. 事件驱动

在Node.js中,客户端请求建立连接、调用接口等行为会触发相应的事件。 事件循环

3.1 主线程

  1. main:启动入口文件,运行主函数。
  2. event pool:检查是否要进入事件循环。
    • 检查其他线程里是否还有待处理事项。
    • 检查其他任务是否还在进行中(比如计时器、文件读取操作等任务是否完成)。
    • 有以上情况,进入事件循环,运行其他任务。 事件循环的过程:从timer 至 close callbacks整个流程走完,到event loop检查是否结束,没结束继续走一圈。
  3. over:所有事件都结束。

3.2 事件循环

事件循环流程图

三、Node.js适用场景

  1. I/O密集型场景
  2. RestFul API
  3. RPC 服务
  4. BBF
  5. Serverless
  6. 微服务