初涉Node.js,吾心有惑:
独线程之系,何以同刻应千请?
似相悖,然既明事件循环之理(尤赖官文),豁然开朗。此篇,吾欲以最简之辞,释其深蕴,不堕浅薄。
何谓“单线程”
Node.js常被称为单线程然此言不备。
- JavaScript之执行,行于一主线程
- 然Node.js其物也非不限于一时一事
-
其用之:
- 操作系统核心
- 后台线程(libuv)
- 异步输入输出
故正确之言曰:
Node.js也单线程以执行JavaScript然则多系統處理輸出入
此别乃一切之本。
核心理念:事件驱动,非阻塞架构
Node.js不待诸务毕
其法若此:
- 受请
- 始务(呼DB、读檄、询API)
- 不待
- 迁次请
- 俟果备而归
是谓之非阻塞 I/O.
→ 据官方文档载:
Node.js 每当可能,皆将操作委诸系统,故主线程得以清闲.
想象之如次(简易譬喻)
试想:
- 汝为侍者(事件循环)
- 庖厨 = 操作系统 / 后台工作者
汝:
- 奉令
- 达庖厨
- 进成馔
尔毋:
- 自庖
- 虚候一令
此即Node.js之扩也
深探:事件循环之序
事件循环非独列次。其行于序 各司其职,分理各类回调。
架构:
主要阶段:
- 计时器
- 执行
setTimeout()与setInterval()
- 中的回调
- 处理系统级回调(如TCP错误)
- 闲置/准备
- 内部使用(非我们所处理之事)
- 投票阶段(至要)
- 索新I/O之变
- 行I/O之回调
- 若无事则待
- 检视阶段
- 行
setImmediate()之回调
- 闭回调
- 行清理之回调(如
socket.on('close'))
┌───────────────────────────┐
│ timers │
└─────────────┬─────────────┘
│
v
┌───────────────────────────┐
┌─>│ pending callbacks │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
│ │ idle, prepare │
│ └─────────────┬─────────────┘ ┌───────────────┐
│ ┌─────────────┴─────────────┐ │ incoming: │
│ │ poll │<─────┤ connections, │
│ └─────────────┬─────────────┘ │ data, etc. │
│ ┌─────────────┴─────────────┐ └───────────────┘
│ │ check │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
│ │ close callbacks │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
└──┤ timers │
└───────────────────────────┘
→ 此循环持续流转不息.
询相为心
此乃玄妙之境.
- 来者之请于此处理.
- 既成之务复归于此.
- 若无待办之事 → 节点从容而待.
故 Node.js 不费 CPU 之循环,而能久持其可扩展之性。
process.nextTick() 與 setImmediate() 之辨
此乃微末而甚要之节,世人多所忽焉。
process.nextTick()
- 施行之方今之职既毕,即行后事。
- 驰骋事毕而轮回复始
- 若滥用,可阻塞I/O
setImmediate()
- 于次迭代中执行(检视阶段)
- 更安全,更可预期
→ 官方文档建议:
多场景下优先选用
setImmediate()
Node.js如何处理千钧之请
此诚要问也.
传统服务器(每请求一线程)
- 每请求数起一缕丝
- 内存之重
- 上下文切换之劳
节点之道
- 单丝应万求
- 每求不生丝
- 以异步回响代之
实际之状:
- 千客叩门
-
节点云:
- 悉录诸请
- 启诸务之异步
- 使事件之循环无滞
-
既得应答:
- 回呼次第列于序
- 事件之循环施之
→ 果:
高并发而资源之用寡
要义:Node.js最宜于I/O之务
Node.js 之辉发于:
- 数据库查询
- API调用
- 文件系统操作
- 流式传输
- 实时应用(如聊天、套接字)
然…
非其所长:
- 繁重CPU计算
- 大型同步循环
盖因:
阻塞事件循环 = 阻塞一切
害事之谬,损功之由
1. 阻塞之码
while(true) {}
→ 整體伺服器凍結
二、误用process.nextTick()
- 可饿其事绪
- 阻隔输入输出之执行
三、于 API 中著同步之码
fs.readFileSync()
→ 避之於生产
若需更大力(超越一核之扩展)
Node.js 每进程单线程,然可藉以下之法以扩展之:
- 集群模块
- 工作线程
- 负载均衡器
→ 允此:
- 多核之用
- 横轴扩展
吾之终悟
阅官方Node.js之文,亲历筑应用,吾之思若此:
- Node.js非求一时之功也
- 彼正欲之莫阻。
事件循环者,不过为聪慧之协调者也。
- 其行所备也
- 越待而逝
- 使系统运行不息
是故也。
Node.js能应数千并请求——非并行执行,而以高效调度与无阻塞设计
终章
若必以一言蔽之
Node.js之可扩展,非因其速,而因其优。不待无谓
一旦此心法通,Node.js之架构,无不明矣。













