公司动态

为什么Node.js的net模块只能跑1.3GB/s?完整解析turbo-net:底层高性能TCP网络库

📅 2026/8/27 17:34:21
为什么Node.js的net模块只能跑1.3GB/s?完整解析turbo-net:底层高性能TCP网络库
为什么Node.js的net模块只能跑1.3GB/s完整解析turbo-net底层高性能TCP网络库【免费下载链接】turbo-netLow level TCP library for Node.js项目地址: https://gitcode.com/gh_mirrors/tu/turbo-netNode.js 里写 TCP 服务net模块是首选但它在高吞吐场景下只能跑到 1.3GB/s。而 turbo-net 这个底层高性能 TCP 网络库通过直接驱动 libuv、复用缓冲区、支持散列写入把同样的 Echo 服务吞吐提升到3.4GB/s内存占用反而从约 100MB 降到约 35MB。本文用通俗的语言带你搞清楚差距从哪来以及 turbo-net 是如何做到的。 先看实测同样的 Echo 服务性能差 2.6 倍项目内置了两个可直接运行的对照示例实现吞吐内存占用示例文件Node.js 原生net模块1.3GB/s约 100MBexamples/echo-classic.jsturbo-net3.4GB/s约 35MBexamples/echo-turbo.js Echo 服务是网络库的体检项目数据原样读进来再写回去没有任何业务逻辑因此吞吐差异几乎全部来自网络层本身的开销。想自己验证克隆仓库即可运行git clone https://gitcode.com/gh_mirrors/tu/turbo-net 为什么 net 模块只跑一半两个隐藏开销net模块好用是因为它站在两层通用设施之上而这两层在极致吞吐场景下都会产生成本1. 高频内存分配每次读数据都新买一块地皮使用net时每当触发data事件底层都会新分配一个 Buffer把数据放进去。数据流越大、越快内存分配器V8 堆 系统 malloc就越忙。对比 examples/echo-classic.js 可以看到经典写法完全依赖框架自动分配的缓冲区——每秒几 GB 的数据流意味着海量的临时 Buffer 出生又死亡垃圾回收压力巨大。这也是为什么经典示例要吃掉约 100MB 内存。2. Stream 封装的间接层net的 Socket 是基于 Node.js 的 Stream 体系构建的数据经过流的事件循环分发、背压控制、编码转换等通用机制。这些机制对通用性很友好但对极致速度来说都是额外的函数调用和数据搬运。 turbo-net 的三板斧把中间层全部拿掉turbo-net 的核心思想很简单跳过 Stream 层让 JS 代码直接与 libuvNode.js 底层的 C 网络库对话。① 缓冲区由你自己提供、反复复用turbo-net 的read接口要求你传入一个 Buffer数据直接写进这块内存见 src/turbo_net.c 中的turbo_net_tcp_read。读完后回调返回你再决定用同一块 Buffer 继续读。官方示例 examples/echo-turbo.js 就是这个套路预分配几块 Buffer 在读 → 写之间循环使用全程几乎零新分配——这就是内存只有 35MB 的原因。② 请求队列复用避免反复创建对象每次读写请求在 C 层需要一个句柄uv_write_t。turbo-net 用了一个巧妙的环形请求队列lib/queue.js请求对象一次性预创建用完即回收通过位掩码mask实现 O(1) 的入队出队。热路径上不再有任何对象分配。③ writev 散列写入一次系统调用发多段数据TCP 一次发送由多段 Buffer 组成时比如协议头 正文普通写法要么拼接成新 Buffer一次拷贝要么发多次系统调用。turbo-net 的writev接口lib/connection.js直接把多段 Buffer 交给 C 层做scatter-gather write见 src/turbo_net.c 的双 Buffer 快速路径turbo_net_tcp_write_two一次系统调用搞定零拷贝。架构一览整个项目结构非常精简一眼能看完整条链路模块作用src/turbo_net.cC 原生层通过 N-API 直接调用 libuv 的 TCP 接口lib/binding.js加载预编译二进制node-gyp-buildlib/server.jsTCP 服务端封装listen、connection 事件lib/connection.js连接封装read / write / writev / end / closelib/queue.js读写请求的环形复用队列lib/lookup.js域名解析纯 IP 直接跳过 DNSexamples/3 个可直接运行的示例test/服务端、连接、读写的完整测试用例⚡ 五分钟上手写出 3.4GB/s 的 Echo 服务npm install turbo-net它保留了与net相似的 API 风格迁移成本很低入口在 index.jsconst turbo require(turbo-net) const server turbo.createServer(function (socket) { socket.read(Buffer.alloc(32 * 1024), function onread (err, buf, read) { if (err) throw err socket.write(buf, read, function (err) { if (err) throw err socket.read(buf, onread) // 复用同一个 Buffer 继续读 }) }) }) server.listen(8080)常用 API 与net的对照turbo-net对应 net 用法说明turbo.createServer(fn)net.createServer(fn)创建服务端支持allowHalfOpen选项server.listen(port, cb)同名还支持server.address()查询实际端口turbo.connect(port, host)net.connect客户端连接支持 DNS 域名socket.read(buf, cb)socket.on(data)关键差异读入你提供的 Buffersocket.write(buf, len, cb)socket.write可指定写入长度socket.writev(buffers, cb)无多段 Buffer 一次写入socket.end() / close()同名半关闭 / 全关闭 注意read的回调参数是(err, buffer, bytesRead)当bytesRead为 0 时表示对端已关闭可读方向。 turbo-net 适合谁适合文件同步 / P2P 传输 / 存储引擎等大流量、低消息频率场景已经熟悉net模块、想压榨最后一滴性能的开发者用 C 扩展写底层、需要绕过 Stream 层的框架开发者它同时支持 N-API 与 N-API 宏跨 Node 版本稳定可能不需要普通 Web 后端、WebSocket 网关等以 HTTP 为主的业务——http/net的性能早已够用追求完全零配置的场景缓冲区管理需要你显式编写这也是它快、也更底层的原因✅ 总结Node.jsnet模块的 1.3GB/s 上限主要卡在高频内存分配与Stream 通用层的间接开销turbo-net 通过自管缓冲区复用、直连 libuv、writev 散列写入三板斧把 Echo 吞吐做到 3.4GB/s内存还降低约 65%API 与net一脉相承几行代码即可替换是 Node.js 生态中少见的、面向极致吞吐的底层 TCP 网络库。如果你的业务瓶颈在数据搬得不够快不妨跑一下 examples/echo-turbo.js 亲自感受一下这台涡轮增压。【免费下载链接】turbo-netLow level TCP library for Node.js项目地址: https://gitcode.com/gh_mirrors/tu/turbo-net创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考