学习路线:从当前进程到可靠的多进程服务
适用版本:Node.js 22 及以上。本教程使用 TypeScript 的
import语法,并按本项目的tsconfig.json编译为 CommonJS;示例可保存为.ts后用ts-node 文件名.ts运行。
本章解决的问题
process、child_process、cluster分别管理什么?- 为什么应先理解标准流、退出码和信号,再启动子进程?
- 后续章节的示例最终会组成什么?
一张总图
操作系统
└─ 当前 Node.js 进程(process)
├─ JavaScript 主线程 + Event Loop
├─ libuv 管理的系统 I/O 和线程池
├─ 外部工具进程(child_process.spawn)
└─ Node.js 子进程(child_process.fork)
cluster Primary(也是 Node.js 进程)
├─ Cluster Worker 1(独立 Node.js 进程)
└─ Cluster Worker 2(独立 Node.js 进程)
process是表示当前 Node.js 进程的全局对象,不需要导入。child_process是创建和管理其他操作系统进程的模块。cluster在child_process之上提供 Primary/Worker 管理以及网络端口共享语义。worker只是“干活者”的角色名。Cluster Worker、Worker Thread、队列 Worker 不是同一种东西。
进程之间默认不共享 JavaScript 变量。可用标准流、Node IPC、Socket、Redis、数据库或消息队列通信。标准流适合父子进程传字节;IPC 适合有关联的 Node.js 进程传结构化消息;跨机器通常应使用外部共享系统。
贯穿项目:分析任务执行服务
教程将逐步设计一个日常常见的服务:HTTP 接口接收任务,Node.js 调用 Bash/Python 工具,保存结果并可优雅停机。
POST /analysis-tasks
↓
创建任务记录
↓
spawn(command, args) 启动工具
↓
stdout:机器可读结果/进度
stderr:诊断日志
↓
等待 close,结合退出码判断成功
↓
保存结果;超时、取消、停机时清理
建议的外部脚本协议:
stdin 可选输入数据
stdout JSON Lines 进度和最终结果
stderr 日志、警告和诊断
exit 0 脚本成功
exit 非 0 脚本失败
例如 stdout:
{"type":"progress","percent":40}
{"type":"result","outputPath":"results/task-123.json"}
学习阶段
00~06:当前进程、参数、标准流、Bash 管道、信号和优雅退出。07~13:创建第三方进程,正确处理输出、失败、超时和清理。14~15:IPC,以及不同worker概念的边界。16~18:Cluster、多进程 Express 和重复副作用。19~21:命令安全、部署与排错。22~23:综合项目和高频 API 速查。
高频 API 预览
process.pid;
process.argv;
process.env;
process.cwd();
process.stdout.write("hello\n");
process.on("SIGTERM", () => {});
// 后续章节会使用:
// spawn(), execFile(), fork(), cluster.fork()
正常与异常时间线
正常 CLI:
读取 argv/env → 执行业务 → 写 stdout → 设置 exitCode=0/自然结束
异常 CLI:
校验失败 → 写 stderr → 设置 exitCode=1 → 让待完成的输出自然收尾
正常服务停机:
收到 SIGTERM → 停接新请求 → 等在途任务 → 关闭资源 → 自然退出
异常做法:
任意位置 process.exit(1) → 尚未写完的日志、响应或文件可能被截断
Windows 与 Linux
- PID、stdin/stdout/stderr 和退出码概念在两者都存在。
- Bash 语法主要面向 Linux/macOS;Windows PowerShell 的变量、引号、管道和退出码语义有差异。
- POSIX 信号在 Linux 上最完整;Windows 只能模拟或支持其中一部分。
- 路径和可执行文件名不同,代码应把命令与参数分开,并通过配置选择平台命令。
常见认识误区
- “Node.js 单线程”不等于一个 Node.js 进程只有一个操作系统线程。
- stderr 有内容不等于程序失败;最终要看退出码和业务结果协议。
child.kill()不等于子进程已经退出。exit不等于子进程 stdio 已经全部关闭;收完整输出通常等待close。- Cluster Worker 的内存、连接池、定时器都相互独立。
最佳实践
- 先制定进程协议,再编写启动代码。
- 日志写 stderr,机器结果写 stdout,成功与失败使用退出码表达。
- 所有长任务都考虑超时、取消、输出上限和清理。
- 日志至少带上
pid;多进程任务再带workerId、taskId、childPid。 - 服务的信号监听和关闭流程集中管理,确保幂等。
练习题
- 用自己的话解释
process、child_process、cluster的职责。 - 设计一个图片转换脚本的 stdin、stdout、stderr 和退出码协议。
- 思考:为什么不能把内存中的
Map当作 Cluster 下的共享任务表?
验收点
- [ ] 能指出 Cluster Worker 本质上是独立进程。
- [ ] 能说出 stdout、stderr 与退出码各自表达什么。
- [ ] 知道贯穿项目的正常完成、取消和停机都要等待资源收尾。