Node.js 计算性能瓶颈和发展
2025-4-27 • 4min
每当提到 Node.js 的计算性能,一定会提到 cluster 和 worker_threads,但这俩并不是什么好的解决方案,只是临时的、打补丁似的思路。
最大的问题就是开销,主要来自于两方面:
首先是通信的损耗,如果用 worker_threads ,主线程和工作线程之间的通信主要通过消息传递。对于大量或复杂数据的传输,数据的序列化、反序列化和复制会带来显著的性能开销。虽然可以使用 SharedArrayBuffer 实现内存共享,但这需要更复杂的同步机制来避免竞态条件。
如果使用 cluster,这个问题更严重,每个工作进程都是一个独立的 Node.js 进程,拥有独立的内存空间。这意味着工作进程之间无法直接共享内存中的数据。如果在应用中需要共享状态(例如用户会话、缓存数据等),需要借助外部存储(如数据库、缓存服务器 Redis 等)或通过 IPC 进行进程间通信来同步数据。
这意味着,即便你构建的是一个巨石架构的单体项目,也需要以大型分布式系统的方式来思考。
第二是 cluster 和 worker_threads 都需要启动独立的 VM 实例,这会带来一定的启动开销和资源消耗。
如果频繁地创建和销毁 Worker,其开销可能会抵消并行带来的好处,为了避免这样的事情发生,最终又需要类似于piscina的池化方案。
那么,有没有办法解决这些问题?
目前有希望的是 Struct 提案。
Struct 提案
proposal-structs(结构体提案)
是 TC39 目前处于 Stage2 的提案,该提案引入了 3 个重要的特性:
- 结构体 结构体是更严格的对象。它们的行为类似于 class 实例,但有更多限制,有利于优化和分析。
- 共享结构体 进一步受限的结构体,可以被多个实例并行共享和访问。它们支持共享内存多线程。
- 互斥锁和条件变量 是用于同步访问共享内存的更高级别的抽象
这个提案解决了我们的第一个问题,那就是通信间的损耗和并发时的内存安全问题。
- 共享结构体可以在多个实例间共享,这意味着我们不需要再担心进程间通信的损耗。
- 互斥锁和条件变量可以让我们在多个线程间安全地共享数据。
如果你对 Struct 提案感兴趣,可以看这里。