
从 ffmpeg 子进程到多核负载均衡Node.js 构建可扩展网络程序的经典设计指南【免费下载链接】nodejs.orgThe Node.js® Website项目地址: https://gitcode.com/GitHub_Trending/no/nodejs.org本文源自 nodejs.org 仓库中的一篇历史性技术博文——由 Node.js 创始人 Ryan Dahl 于 2011 年撰写的 An Easy Way to Build Scalable Network Programs。文章以视频编码服务器为具体场景系统阐述了 Node.js 的核心设计哲学将 I/O 密集与计算密集任务分离、以多进程吃满多核、把调度交给内核。读完本文你将掌握 Node.js 之所以为可扩展网络程序而生的三大设计原则并了解这些理念如何在 libuv、cluster 模块的演进中落地以及这篇 2011 年的文章在今天 nodejs.org 网站中是如何被保存与渲染的。一场关于Node 能否做视频编码的争论文章开篇设定了一个非常具体的场景假设你在编写一个 Web 服务器每次文件上传后都需要做视频编码video encoding。视频编码是典型的**计算密集型compute bound**任务会长时间占用 CPU。彼时不少博客文章断言Node.js 在这种负载下会惨败fail miserably。Ryan Dahl 的回应直指要害——使用 Node 并不意味着你必须用 JavaScript 去实现视频编码算法他甚至调侃 JavaScript 连 64 位整数都不原生支持更不意味着要在主服务器的事件循环event loop里做繁重的计算。这一争论的实质是早期对单线程事件驱动模型是否适合 CPU 密集任务的普遍误解。文章给出的答案不是让 Node 变快而是重新划定任务边界Node 的价值在于高效处理 I/O 边界而非取代内核去调度计算。设计原则一把 I/O 密集与计算密集彻底分离文章给出了应对视频编码场景的推荐架构将接收上传、提供下载这类 I/O 密集任务与视频编码这类计算密集任务分离。接收上传与提供下载交给 Node 主进程的事件循环享受其非阻塞 I/O 带来的高并发能力视频编码fork 出外部进程例如ffmpeg执行Node 通过**异步子进程控制asynchronously controlling subprocesses**能力来编排这一外部计算任务。原文明确写道Node 为此类工作提供了先进的异步子进程控制手段。这一模式在今日仍是处理 CPU 密集任务的经典套路不要把重计算塞进事件循环而是把它外包给子进程让 Node 只负责调度、通信与结果汇聚。实战要点子进程方案为何优于自己算主事件循环不被阻塞其他请求的吞吐不受单个编码任务影响计算进程崩溃不会拖垮服务器主进程从进程模型看这是天然的故障隔离可以利用系统上现成、经过深度优化的编码工具如 ffmpeg而非在脚本语言里重造轮子。设计原则二用多进程吃满多核当时对 Node 的另一个常见质疑是Node 无法利用多核机器。文章对此给出反驳——Node 早已支持用寥寥几行代码在多进程之间对连接做负载均衡load-balancing connections over multiple processes这样 Node 服务器就能用上所有可用核心。更值得注意的是文章披露的路线图在即将到来的版本中我们会让它更简单只需在命令行传入--balanceNode 就会自动管理进程集群cluster of processes。结合仓库中的发布记录可以还原这条路线图的最终形态Node.js 0.6.0 发布说明2011 年 11 月明确列出新特性Integrated load balancing over multiple processes即在多进程之上集成负载均衡并链接了cluster模块文档Node.js 0.11.2 发布说明2013 年 5 月进一步记录cluster: use round-robin load balancing即 cluster 模块引入轮询round-robin负载均衡策略。从源码记录看--balance命令行开关最终并未以独立标志的形式落地取而代之的是cluster模块这一更完善的载体——它把多进程 连接分发封装成了几行代码即可使用的 API。这正好兑现了文章让多核利用变得简单的承诺只是实现路径从启动标志演进为内置模块。设计原则三Node 的边界与清晰定位文章用一段非常宣言式的文字界定了 Node.js 的用途Node 有明确的目的提供一种构建可扩展网络程序的简单方式an easy way to build scalable network programs它不是解决所有问题的工具明确的反例不要用 Node 写光线追踪器ray tracer不要用 Node 写 Web 浏览器明确的正例如果要写DNS 服务器、DHCP 服务器甚至是视频编码服务器请放心选择 Node。这条边界线至今仍有指导意义Node 擅长的是面向连接、面向消息、以 I/O 为重心的网络程序而不是以 CPU 浮点运算为重心的工作负载。判断一个任务是否适合 Node可以问自己这个任务的主要瓶颈是等待网络、磁盘、外部服务还是计算本身前者是 Node 的舒适区后者则需要借助子进程/多进程来隔离。底层哲学把调度权交给内核文章揭示了 Node 设计上的一种朴素哲学通过依赖内核来调度和抢占schedule and preempt计算代价高昂的任务、并在多个进程间做入站连接的负载均衡Node 看起来不如那些采用用户态调度userland scheduling的服务器平台那样魔法。这段话包含两个关键判断透明的力量Node 不搞用户态的自定义调度器而是把进程调度、CPU 抢占、连接分发交给操作系统内核。这让它的行为更可预测、更容易理解——我们的关注点始终是简单与透明simplicity and transparency反魔法设计看似朴素的方案反而让开发者能准确预估系统在压力下的表现也降低了排障成本。文章最后提到当时已有越来越多的开发者和公司采用 Node 并取得成功文中以链接形式列举了若干成功案例。这些案例构成了对上述设计哲学的市场验证。周边证据libuv 与子进程能力的底层支撑文章中提到的异步子进程控制在同时期的仓库文档中能找到底层实现依据。在 libuv 状态报告Ryan Dahl2011 年 9 月比本文早约两周中libuv 当时已实现的功能清单里明确包含非阻塞 TCP 套接字Windows 上基于 IOCP非阻塞命名管道UDP定时器子进程派生Child process spawning异步 DNS基于 c-ares 的uv_getaddrinfo异步文件系统 APIuv_fs_*高精度时钟uv_hrtime线程池调度uv_queue_work。而在正在开发列表中还有进程间套接字共享uv_ipc_t一项——这正与本文多进程负载均衡连接的设计相呼应要把入站连接公平地分发到多个 Node 进程进程间共享/传递套接字是底层基础设施。换言之本文描述的上层编程模型子进程 多进程负载均衡在底层由 libuv 的平台抽象能力支撑。这篇 2011 年的文章在今天如何被呈现作为历史档案这篇文章被完整保存在 nodejs.org 仓库中并且依然通过现代的博客管线被构建和渲染。文件与 frontmatter文章位于 apps/site/pages/en/blog/uncategorized/an-easy-way-to-build-scalable-network-programs.md其 frontmatter 展示了仓库博客文件的元数据约定date: 2011-10-04T22:39:56.000Z category: uncategorized title: An Easy Way to Build Scalable Network Programs layout: blog-post author: Ryan Dahl这与 types/frontmatter.ts 中定义的Frontmatter类型一致layout、title、date、author、category等字段均可选。layout: blog-post会将该文章路由到 layouts/Post.tsx 布局——该布局负责渲染标题、作者头像组、正文预览Preview与文末交叉链接。博客数据的生成apps/site/scripts/blog-data/generate.mjs 展示了仓库如何把数千篇历史博客变成结构化数据通过getMarkdownFiles定义于 next.helpers.mjs用 glob 枚举pages/en/blog下的全部.md/.mdx用createReadStreamreadline逐行读取遇到第二个---分隔符即停止——只解析 frontmatter不读正文以优化海量文件的读取性能用gray-matter解析元数据并为每篇文章生成三类分类实际分类如uncategorized、year-2011按发布时间自动生成年份分类以及all最终生成slug /blog/{category}/{filename}写入public/blog-data.json。也就是说本文的 URL 结构/blog/uncategorized/an-easy-way-to-build-scalable-network-programs正是由这套管线自动推导的。动态路由与静态化apps/site/app/[locale]/blog/[...path]/page.tsx 负责博客文章的请求处理通过generateMetadata生成页面元数据供搜索引擎与社交预览使用声明export const dynamic force-static强制静态渲染同时revalidate 3005 分钟使内容定期失效重建最终按frontmatter.layout选择布局本文为blog-post即Post布局完成渲染。给现代开发者的实践启示把 2011 年的这篇短文映射到今天的工程实践可以提炼出三条仍然有效的行动准则识别瓶颈类型再选择工具I/O 密集网关、代理、实时消息、API 服务器优先选择 Node 的事件循环模型计算密集音视频转码、图像处理、复杂算法应拆到子进程、worker 或独立服务中执行用进程/worker 模型利用多核文章所预告的多进程负载均衡已经演进为成熟的cluster模块v0.6.0 集成、v0.11.2 引入 round-robin 策略配合child_process.fork等 IPC 机制可在保持事件驱动优势的同时吃满机器资源保持架构透明把调度、抢占与连接分发交给内核避免自造魔法调度器——简单的系统更容易预测、调优与排障。一篇文章在十五年后依然被官方网站完整保存、构建与访问本身就是对它所阐述理念的最好注脚Node.js 的目标始终清晰——为可扩展的网络程序提供一种简单的方式。至于它能做什么、不该做什么答案早在 2011 年就写在了这篇经典文章中。【免费下载链接】nodejs.orgThe Node.js® Website项目地址: https://gitcode.com/GitHub_Trending/no/nodejs.org创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考