Skip to content

Node.js 架构分析

高层架构

Node.js 采用单线程、事件驱动的架构,整体可以分为以下几个层次:

┌─────────────────────────────────────────────────┐
│           应用层 (JavaScript)                  │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐   │
│  │  用户代码 │  │  npm 模块 │  │  核心模块 │   │
│  └──────────┘  └──────────┘  └──────────┘   │
├─────────────────────────────────────────────────┤
│           Node.js 绑定层 (C++)                 │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐   │
│  │  Buffer  │  │  Stream  │  │  Events  │   │
│  └──────────┘  └──────────┘  └──────────┘   │
├─────────────────────────────────────────────────┤
│           V8 引擎 (JavaScript 引擎)            │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐   │
│  │  JIT 编译│  │ 垃圾回收 │  │  对象模型 │   │
│  └──────────┘  └──────────┘  └──────────┘   │
├─────────────────────────────────────────────────┤
│           libuv (异步 I/O 库)                 │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐   │
│  │ 事件循环 │  │  线程池  │  │ 异步 I/O │   │
│  └──────────┘  └──────────┘  └──────────┘   │
├─────────────────────────────────────────────────┤
│           操作系统层                            │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐   │
│  │  文件系统│  │  网络协议 │  │  系统调用 │   │
│  └──────────┘  └──────────┘  └──────────┘   │
└─────────────────────────────────────────────────┘

核心模块

事件循环 (Event Loop)

事件循环是 Node.js 的核心机制,它负责协调异步操作的执行。事件循环的工作流程如下:

┌───────────────────────────────┐
│     timers (定时器)          │
│   setTimeout, setInterval     │
└───────────────┬─────────────┘


┌───────────────────────────────┐
│   pending callbacks (待定回调) │
│   I/O 回调、错误回调          │
└───────────────┬─────────────┘


┌───────────────────────────────┐
│   idle, prepare (空闲/准备)    │
│   内部使用                    │
└───────────────┬─────────────┘


┌───────────────────────────────┐
│     poll (轮询)              │
│   执行 I/O 回调              │
└───────────────┬─────────────┘


┌───────────────────────────────┐
│     check (检查)             │
│   setImmediate 回调           │
└───────────────┬─────────────┘


┌───────────────────────────────┐
│   close callbacks (关闭回调)  │
│   socket.close 等回调         │
└───────────────┬─────────────┘


           (循环继续)

事件循环阶段详解

  1. timers 阶段:执行 setTimeout 和 setInterval 的回调
  2. pending callbacks 阶段:执行某些系统操作的回调
  3. idle, prepare 阶段:仅内部使用
  4. poll 阶段:获取新的 I/O 事件,执行 I/O 回调
  5. check 阶段:执行 setImmediate 的回调
  6. close callbacks 阶段:执行关闭的回调函数

异步 I/O 模型

Node.js 的异步 I/O 模型基于 libuv,其工作原理如下:

用户代码


调用异步 API (如 fs.readFile)


注册回调函数到事件队列


libuv 将 I/O 操作提交给操作系统


操作系统执行 I/O 操作(非阻塞)


I/O 操作完成,操作系统通知 libuv


libuv 将回调函数放入事件队列


事件循环从队列中取出回调函数执行


回调函数执行完成

模块系统

Node.js 使用 CommonJS 模块系统,其工作流程如下:

┌─────────────────────────────────────┐
│  require('./module.js')            │
└───────────────┬───────────────────┘


┌─────────────────────────────────────┐
│  解析模块路径                      │
│  - 核心模块 (如 fs, http)          │
│  - 相对路径 (如 ./module.js)       │
│  - 绝对路径 (如 /path/to/module)   │
│  - node_modules                   │
└───────────────┬───────────────────┘


┌─────────────────────────────────────┐
│  检查模块缓存                      │
│  - 如果已缓存,直接返回导出对象     │
└───────────────┬───────────────────┘


┌─────────────────────────────────────┐
│  加载模块                          │
│  - 读取文件内容                    │
│  - 包装在函数中                    │
│  - 执行模块代码                    │
│  - 缓存导出对象                    │
└───────────────┬───────────────────┘


┌─────────────────────────────────────┐
│  返回模块导出对象                  │
└─────────────────────────────────────┘

数据流

HTTP 请求处理流程

客户端请求


┌─────────────────────────────────────┐
│  HTTP 解析器                        │
│  - 解析请求行                       │
│  - 解析请求头                       │
│  - 解析请求体                       │
└───────────────┬───────────────────┘


┌─────────────────────────────────────┐
│  HTTP 服务器                        │
│  - 创建 Server 对象                 │
│  - 触发 request 事件                │
│  - 调用请求处理器                   │
└───────────────┬───────────────────┘


┌─────────────────────────────────────┐
│  应用逻辑                           │
│  - 处理请求参数                     │
│  - 执行业务逻辑                     │
│  - 生成响应数据                     │
└───────────────┬───────────────────┘


┌─────────────────────────────────────┐
│  响应生成                           │
│  - 设置响应头                       │
│  - 写入响应体                       │
│  - 结束响应                         │
└───────────────┬───────────────────┘


客户端响应

流处理流程

可读流 (Readable)


┌─────────────────────────────────────┐
│  数据源                            │
│  - 文件                            │
│  - 网络请求                        │
│  - 内存缓冲区                      │
└───────────────┬───────────────────┘


┌─────────────────────────────────────┐
│  数据块 (Chunk)                    │
│  - 触发 data 事件                   │
│  - 传递数据块                      │
└───────────────┬───────────────────┘


┌─────────────────────────────────────┐
│  转换流 (Transform) - 可选         │
│  - 修改数据                        │
│  - 转换格式                        │
└───────────────┬───────────────────┘


┌─────────────────────────────────────┐
│  可写流 (Writable)                 │
│  - 接收数据块                      │
│  - 写入目标                        │
└───────────────┬───────────────────┘


┌─────────────────────────────────────┐
│  数据目标                          │
│  - 文件                            │
│  - 网络响应                        │
│  - 内存缓冲区                      │
└─────────────────────────────────────┘

并发模型

单线程模型

Node.js 使用单线程模型处理 JavaScript 代码:

主线程 (JavaScript)
┌─────────────────────────────────────┐
│  执行 JavaScript 代码             │
│  - 同步操作                        │
│  - 回调函数                        │
│  - 事件处理器                      │
└───────────────┬───────────────────┘


线程池 (libuv)
┌─────────────────────────────────────┐
│  处理阻塞操作                      │
│  - 文件系统操作                    │
│  - DNS 解析                        │
│  - 加密操作                        │
└─────────────────────────────────────┘

并发处理

虽然 JavaScript 是单线程的,但 Node.js 可以处理大量并发:

客户端 1 ──┐
           ├──► 事件循环 ──► 主线程
客户端 2 ──┤          │
           │          ▼
客户端 3 ──┤      线程池 (处理阻塞操作)

客户端 N ──┘

容错策略

错误处理

Node.js 提供多种错误处理机制:

  1. try-catch:捕获同步错误
  2. error 事件:处理异步错误
  3. uncaughtException:捕获未处理的异常
  4. unhandledRejection:捕获未处理的 Promise 拒绝

进程管理

Node.js 提供进程管理功能:

  • cluster 模块:创建多个工作进程
  • child_process:创建子进程
  • process 对象:访问当前进程信息

可扩展性杠杆

水平扩展

Node.js 支持多种水平扩展方式:

  1. 负载均衡:使用 Nginx、HAProxy 等负载均衡器
  2. 集群模式:使用 cluster 模块创建多进程
  3. 微服务:拆分为多个独立的服务
  4. 容器化:使用 Docker 进行容器化部署

垂直扩展

Node.js 也支持垂直扩展:

  1. 增加 CPU 核心:利用多核 CPU
  2. 增加内存:提高内存限制
  3. 优化代码:减少 CPU 和内存使用
  4. 使用缓存:减少重复计算和 I/O 操作

总结

Node.js 的架构设计体现了"简单、高效、可扩展"的理念。通过事件循环、异步 I/O 和模块系统,Node.js 提供了一个强大的服务器端 JavaScript 运行时环境。理解 Node.js 的架构有助于我们更好地使用和优化 Node.js 应用。


架构师AI杜公众号二维码

扫描二维码关注"架构师AI杜"公众号,获取更多技术内容和最新动态