Node.js 架构理解

0 阅读4分钟

一.核心结论速记


一句话概括 Node.js 架构:

单线程主线程负责接活、派活、跑回调,多线程 libuv 线程池负责干慢活累活,主线程全程不阻塞。


二.分层拆解


1. 第一层:你的应用代码(Application)

就是你写的 .js 文件,比如 fs.readFilehttp.createServer 这类业务代码。

  • 作用:定义业务逻辑,发起各种操作请求(同步/异步)。
  • 关系:所有代码最终都会交给 V8 引擎执行。

2. 第二层:V8 引擎(JavaScript 引擎)

  • 它是 Google 开发的 JS 引擎,Node.js 用它来执行 JS 代码。
  • 核心特点:单线程执行,同一时间只能处理一件事。
  • 打比方:就像餐厅里唯一的大厨,同一时间只能炒一道菜。

3. 第三层:Node.js Bindings(Node API 绑定层)

  • 它是「翻译官+联络员」,连接 V8 和底层系统/libuv。
  • 作用:
    1. 把 JS 调用(比如 fs.readFile)翻译成操作系统能懂的指令。
    2. 把耗时的 I/O 操作,交给 libuv 处理,不让 V8 阻塞。
  • 打比方:餐厅的传菜员,把顾客的点单传给后厨,再把做好的菜端给大厨/顾客。

4. 第四层:libuv(异步 I/O 核心)

这是 Node.js 实现异步非阻塞的关键,包含两大核心组件:

① 事件循环(Event Loop)

  • 运行在主线程,和 V8 共用同一个线程。
  • 核心作用:循环检查「待办事项」,保证主线程不空闲
  • 它会按顺序检查:
    1. 有没有新的同步代码要执行?
    2. 异步操作的结果有没有返回?(比如文件读完了、网络请求完成了)
    3. 把就绪的回调函数拿出来,交给 V8 执行。
  • 打比方:大厨的“耳朵和眼睛”,炒完一道菜,就听有没有帮工喊“菜做好了”,然后继续处理下一个任务。

② 线程池(Worker Threads)

  • 专门处理阻塞/耗时任务的多线程池,默认 4 个线程(可通过 UV_THREADPOOL_SIZE 调整)。
  • 哪些任务会交给它?
    • 文件系统操作(fs.readFile、fs.writeFile 等)
    • 加密解密(crypto 模块)
    • 数据压缩(zlib 模块)
    • DNS 解析(dns.lookup)
  • 处理流程:主线程把慢活扔给线程池,线程池并行处理,干完后把结果和回调函数扔回事件队列,由主线程执行回调。
  • 打比方:餐厅的后厨帮工,负责洗菜、切肉、煮汤这些慢活,多个人一起干,不耽误大厨炒菜。

补充:网络 I/O 不走线程池

  • httptcpwebsocket 这类网络请求,libuv 会用操作系统的「多路复用」技术(epoll/kqueue/IOCP)处理。
  • 全程在主线程处理,不依赖线程池,所以 Node.js 天生适合高并发网络服务。

三.完整流程串讲


看这段代码:

console.log('点餐开始');

fs.readFile('./big-file.txt', () => {
  console.log('文件读完了!上菜');
});

console.log('点完别的菜了');
  1. 同步代码执行阶段
    • 主线程(V8)先执行 console.log('点餐开始'),打印这句话。
    • 遇到 fs.readFile,知道这是慢活,通过 Bindings 交给 libuv 线程池处理,主线程继续往下走。
    • 主线程接着执行 console.log('点完别的菜了'),打印这句话。
  1. 线程池干活阶段
    • 线程池里的帮工开始读文件,主线程这边完全不卡,继续处理其他请求。
  1. 回调执行阶段
    • 帮工读完文件,把结果和回调函数扔到事件队列里。
    • 主线程把所有同步代码都执行完后,事件循环开始检查队列,发现有就绪的回调,就拿过来执行,打印 文件读完了!上菜

四.关键误区澄清


误区正确理解
Node.js 是多线程的主线程(V8+事件循环)是单线程,只有线程池是多线程的
所有异步操作都走线程池网络 I/O 不走线程池,只有文件、加密等阻塞任务才走
线程池越多,性能越好线程池过大会导致线程切换开销增加,默认 4 个已能满足大部分场景

五.问题解读


如果是Node向其他服务器发送请求会经过LIBUV吗?

会经过LIBUV,但是不经过线程池,因为LIBUV通过多路复用,所有的网络请求都是单线程处理,只有文件 / 加密 / DNS 才走 LIBUV 线程池。

你的 JS 代码
   ↓
V8 引擎
   ↓
Node Bindings
   ↓
【 libuv 】← 网络请求全在这里处理!
   ↓
操作系统
方式谁发的走谁的网络走 libuv 吗
Vue axios浏览器浏览器内核❌ 不走
Node http/axiosNode 服务Node 内核一定走
文件读写NodeNode✅ 走 libuv 线程池
网络请求NodeNode✅ 走 libuv 多路复用

💡提示:不管是 Node 接收请求,还是 Node 发送请求,全都走 libuv!而且全都不走线程池,全是主线程 + 多路复用!

六.一句话终极总结

主线程单线程接活、派活、跑回调,libuv 线程池多线程干慢活,主线程全程不阻塞,这就是 Node.js 高性能的核心秘密。