Node.js 26.5.0 现已发布。这是一个 Current 版本,而非 LTS 分支,因此它代表了该平台能力的最新前沿。这种区别非常重要。你不应该盲目地将其切换到期望获得 18 个月稳定性的生产环境集群中。但这些规模较小、迭代迅速的版本正是观察未来走向的地方。它们展示了维护者正在完善哪些 API,以及运行时下一步的走向。在 26.5.0 中,核心工作集中在 Web Streams API 上,通过两项针对性的修复,推动 Node.js 向浏览器标准进一步靠拢。该版本还清理了文件系统和 URL 处理层中的两个瑕疵。
Web Streams 在 Node.js 中起什么作用?
如果你在 Node 中编写流式代码已经有一段时间了,你就会知道内置的 stream 模块有其独特的个性。多年来,Readable、Writable、Transform 和 Duplex 一直是生态系统中的主力。它们功能强大,但与你在浏览器中遇到的流并不相同。当你试图在前端 Service Worker 和后端路由处理器之间共享逻辑时,这种差异就会变成阻碍。你最终不得不重写适配器、将数据复制成陌生的格式,或者干脆完全避免共享代码。
Web Streams API 的存在就是为了弥补这一差距。它是驱动浏览器中 fetch 请求体处理的同一标准。通过将其引入 Node,该项目让你能够编写一次流式逻辑,并将其运行在任何一种环境中。该 API 使用 ReadableStream、WritableStream 和 TransformStream 对象,通过统一的接口传递数据块。26.5.0 版本并没有重写该接口,但它加固了两个重要的环节。
修复 releaseLock 并清理 BYOB 读取器
本次发布的一个具体变化是修复了 WritableStreamDefaultWriter 上的 releaseLock 方法。在 Web Streams 模型中,写入器锁(writer lock)可以防止多个消费者同时争抢同一个流。当你调用 releaseLock() 时,你是在发出信号,表明你的写入器已完成工作,底层流可以进行下一个操作。这里的实现缺陷可能会使流处于一种“悬而未决”的状态,即使写入器已经消失,流仍认为自己被占用着。在解析上传内容或将数据管道传输到存储设备的繁忙服务器中,这种陈旧的锁可能会导致流水线停滞,或抛出难以追溯源头的错误。26.5.0 中的修复让这种交接重新变得可预测。
第二个 Web Streams 的变化改进了 ReadableStream 和 TransformStream 与 BYOB 读取器的工作方式。BYOB 代表“自带缓冲区”(Bring Your Own Buffer)。流不再是每次交付数据时都分配一块新的内存,而是由你提供一个预先准备好的缓冲区。流会填充该缓冲区,你处理字节,然后将同一个缓冲区交还以供复用。这是一种微小的机制差异,但在处理海量数据时会产生巨大的性能收益。
Node 已经支持 BYOB 一段时间了,但在挂载 BYOB 读取器时,ReadableStream 和 TransformStream 的某些边缘情况可能会出现异常。修复的具体细节并不如实际结果重要:现在,使用显式缓冲区的流在读取和转换阶段都更加可靠了。如果你之前因为背压(backpressure)事件期间出现的奇怪错误而避开 BYOB 读取器,那么这次发布又为你提供了一个不再回避它的理由。
BYOB 实际的应用场景
空谈缓冲区复用很容易,但思考它在何处发挥作用则更有意义。
想象你正在编写一个接收遥测数据上传的服务。这些上传内容可能是压缩日志或原始传感器转储,每个都有几百 MB。如果流为每一段数据都分配一个新的 Node Buffer,垃圾回收器(garbage collector)就会超负荷工作,内存峰值也会迅速攀升。使用 BYOB 读取器,你可以在启动时分配一个适度的缓冲区池。流会填充它们,你的解析器消耗它们,然后它们循环使用。内存保持平稳。当你需要在两个套接字(socket)之间代理网络流量,或者逐行解析大型 CSV 文件而不将其全部加载到内存中时,同样的模式也适用。
转换流(Transform streams)在这里也同样重要。TransformStream 位于流水线的中间,可能用于解压 gzip 流或实时加密数据块。如果转换步骤处理 BYOB 缓冲区不当,可能会导致输出损坏、数据块丢失或在高负载下出现停顿。26.5.0 的修复正是针对这类流水线问题,因此任何运行高吞吐量 I/O 的用户都应予以关注。
低调的改进:文件系统与错误清晰度
26.5.0 中的内容并不全是关于流的。该版本还修复了当 recursive 选项设置为 false 时,fs.rm 和 fs.rmSync 的行为。此前,在提供目录路径的同时传递 recursive: false 可能会在清理过程中产生意外结果。该方法的操作可能与调用者的明确意图不符,例如删除超出预期的内容,或者根据平台的不同以不一致的方式失败。文件清理本应是一种枯燥且可预测的操作。枯燥是好事。此次修复恢复了这种可预测性,使得您的临时目录清理脚本或部署拆卸逻辑能够完全按照代码逻辑运行。
在 URL.canParse 报告失败的方式上,也有了一项提升开发体验的改进。该方法用于检查字符串是否为有效的 URL,而不会在输入格式错误时抛出异常。在 26
