Node.js Streams 完全指南:流式处理的艺术

Stream 是 Node.js 的核心超能力:分块处理数据,内存占用与数据规模解耦。本文讲透四种流、管道、错误处理、背压机制与实战案例。

最佳实践
事件循环与异步任务流转插画

流(Stream)让数据分块流动而非整体加载——处理 10GB 文件内存占用恒定。核心 API 是 readable.pipe(writable):管道自动处理背压(下游慢了上游自动暂停)。四种流:Readable、Writable、Duplex、Transform,pipeline() 是错误安全的管道写法。

为什么流是核心超能力

// 反例:10GB 文件读进内存 → 进程爆炸
const data = fs.readFileSync('huge.log');

// 流式:恒定内存,处理 TB 级也从容
fs.createReadStream('huge.log').pipe(fs.createWriteStream('copy.log'));

Node 的一切 IO 都是流:HTTP 请求/响应、文件读写、TCP socket——理解流才算理解 Node。

四种流

  • Readable(可读):数据源——文件读取、HTTP 请求体;
  • Writable(可写):数据终点——文件写入、HTTP 响应;
  • Duplex(双工):既可读又可写——TCP socket;
  • Transform(转换):读写中变换数据——压缩、加密、格式转换。
const { Transform } = require('node:stream');

const upperCase = new Transform({
  transform(chunk, encoding, callback) {
    callback(null, chunk.toString().toUpperCase());
  },
});

process.stdin.pipe(upperCase).pipe(process.stdout);

管道与错误处理

pipe() 的经典缺陷:中间任何流报错,管道不会自动清理其他流。用 pipeline() 替代:

const { pipeline } = require('node:stream/promises');
const zlib = require('node:zlib');

await pipeline(
  fs.createReadStream('input.txt'),
  zlib.createGzip(),
  fs.createWriteStream('input.txt.gz'),
);
// 任一环节出错,全部流正确销毁,错误向上抛出

新项目一律 pipeline,告别裸 pipe。

背压(Backpressure)

下游消费慢于上游生产时,数据在缓冲区堆积——背压机制让上游"等等":write() 返回 false 表示缓冲已满,监听 drain 事件恢复写入。pipe/pipeline 自动处理背压——手动 write 时才需要关心。

实战:大文件处理

const readline = require('node:readline');

// 逐行处理 30GB 日志
const rl = readline.createInterface({ input: fs.createReadStream('access.log') });

let errorCount = 0;
for await (const line of rl) {
  if (line.includes('"status":500')) errorCount++;
}

for await 消费流是现代 Node 的最优雅姿势——流即异步迭代器。

性能要点

  • 分块大小(highWaterMark):默认 64KB(文件 16KB),吞吐型场景可调大;
  • 对象模式(objectMode: true):流传递对象而非 Buffer——任务队列、记录处理用;
  • Transform 链保持薄:每段转换都是一次数据拷贝。

常见问题(FAQ)

Q:流和直接读写的性能差多少?

小文件直接读写更快(少一层抽象);大文件优先采用流或分块处理。阈值应按内存预算、并发量和处理方式测量,不采用固定比例。

Q:流的 destroy 和 close 区别?

destroy() 立即销毁并触发 error 处理;end() 优雅结束(写完缓冲再关)。pipeline 出错时自动 destroy 全链。

Q:Node 流和 Web Streams 什么关系?

两套 API 各有历史(Node 流更早),Node 18+ 两者互转(Stream.toWeb/fromWeb)。详见本系列《Node.js Streams 与 Web Streams 对比》。


参考文档

本文依据公开文档整理,示例需按项目依赖版本验证;不将文档中的性能描述视为本项目实测结果。

延伸阅读

获取专属方案

联系我们

加入社区

微信扫码
加入官方交流群

立即体验

在线开通,按量计费,真正的云服务!

立即开始

选择观测云版本

代码托管平台