Cloudflare官方
25-11-14 17:52 微博认证:Cloudflare官方微博

持续提升 Cloudflare Workers 的 Node.js 兼容性(一)

我们始终将与广泛的 JavaScript 开发者生态系统保持兼容性视为重要的战略投资方向。我们坚信开放标准和开放网络的价值,希望您能将 Workers 视为您开发平台的强大扩展,只需将代码放入其中即可运行。为了实现这一目标,Cloudflare Workers 团队在过去一年里大幅提升了与 Node.js 生态系统的兼容性,使得数百(甚至数千)个广受欢迎的 npm 模块如今都能无缝运行,其中就包括备受青睐的 Express 框架。
我们已经实现了 Node.js 标准库的相当大一部分内容,重点覆盖了最常用、也是开发者最常请求的 API。这些 API 包括:

图2

我们对每一项功能都进行了精心实现,力求在可行范围内最大程度还原 Node.js 的行为特性。对于那些无法完全匹配 Node.js 行为的场景,我们的实现会在调用时抛出明确的错误提示,而不会选择静默失败或直接不提供该功能。这样一来,即使某些功能不可用,那些检查这些 API 是否存在的包也不会出现运行时错误。

在某些情况下,我们不得不在运行时内部实现全新的能力,以便提供所需的功能。例如,针对 node:fs,我们在 Workers 环境中新增了一个虚拟文件系统。而在其他一些情况下,比如对于 node:net、node:tls 和 node:http,我们将新的 Node.js API 封装在了现有的 Workers 能力之上,例如 Sockets API 和 fetch。

最重要的是,所有这些实现均原生集成于 Workers 运行时环境,通过 TypeScript 与 C++ 的协同开发完成。我们之前的 Node.js 兼容性工作严重依赖于部署时通过开发者工具(例如 Wrangler)注入的 polyfill 和 shim,而我们现在正转向一种新模式:未来的 Workers 将原生内置这些 API,无需任何额外依赖。这样做不仅能显著提升性能、降低内存占用,还能确保其行为尽可能贴近原生 Node.js 环境。

网络堆栈

Node.js 提供了丰富的网络 API,允许应用程序创建服务器、发起 HTTP 请求、操作原始 TCP/UDP 套接字、发送 DNS 查询等多种功能。然而 Workers 无法直接访问底层内核级套接字,那么我们该如何支持这些 Node.js 网络 API,确保相关软件包仍能按预期运行呢?我们的解决方案是:基于现有的托管套接字和 fetch API 进行构建。通过这些实现方案,大量依赖网络 API 的主流 Node.js 软件包都能在 Workers 环境中无缝运行。

让我们从 HTTP API 开始:

HTTP 客户端和服务器支持

自从我们宣布将在 Workers 中推进 Node.js 兼容性以来,用户们就一直在特别呼吁实现 node:http 模块。生态系统中有着无数模块,它们直接依赖于诸如 http.get(...) 和 http.createServer(...) 这样的 API。

node:http 和 node:https 模块提供了用于创建 HTTP 客户端和服务器的 API。我们已经实现了这两个模块,允许您使用 http.request() 创建 HTTP 客户端,使用 http.createServer() 创建 HTTP 服务器。HTTP 客户端的实现 基于 Fetch API 构建,而 HTTP 服务器的实现则基于 Workers 运行时现有的请求处理能力构建。

客户端侧相当简单:图3

服务端的使用同样简单,甚至可能更加令人兴奋。我们经常被问及是否能在 Workers 中支持 Express、Koa 或 Fastify,但由于这些框架高度依赖 Node.js 的 API,过去一直很难实现。随着新功能的加入,现在您已可以在 Workers 中使用 Express 和 Koa,我们也有望在未来添加对 Fastify 的支持。

图4

来自 cloudflare:node 模块的 httpServerHandler() 函数,能够将 HTTP server 与 Workers 的 fetch 事件进行集成,从而允许其处理传入请求。

node:dns 模块

node:dns 模块提供了一个 API,用于执行 DNS 查询。

在 Cloudflare,我们恰好提供了一项 DNS-over-HTTPS (DoH) 服务,同时我们还运营着自己的 DNS 服务——1.1.1.1。在向 Workers 暴露 node:dns 模块时,我们充分利用了这一优势。当您使用该模块发起 DNS 查询时,它将直接向 1.1.1.1 发起一个子请求来解析查询。如此一来,用户无需关心 DNS 服务器的配置,查询也能顺利执行。

node:net 和 node:tls 模块

node:net 模块提供了用于创建 TCP 套接字的 API,而 node:tls 模块提供了用于创建安全 TLS 套接字的 API。如前所述,这两个模块都基于现有的 Workers Sockets API 构建。请注意,并非所有 node:net 和 node:tls 模块的功能都可在 Workers 中使用。例如,目前尚无法使用 net.createServer() 创建 TCP 服务器(但可能很快就会实现!),但我们已经实现了足够多的 API,足以支持许多依赖这些模块的热门软件包在 Workers 中使用。

图5

一种新的虚拟文件系统和 node:fs 模块

在无服务器环境中支持文件系统 API 意味着什么?当您部署一个 Worker 时,它可以在“全球区域”(Region:Earth) 上运行,我们不希望您去操心一个个带有独立文件系统的服务器。然而,在生态系统中存在着无数现有的应用程序和模块,它们利用文件系统来存储配置数据、读写临时数据等等。

Workers 与传统的 Node.js 进程不同,它们无法访问传统文件系统——而这样的设计其实大有讲究!这是因为单个 Worker 并非在某台固定的机器上运行,您发起的每一次 Worker 请求,都可能在 Cloudflare 全球网络中任意一台服务器上执行。若强行协调并同步访问传统文件系统这类共享物理资源,不仅会带来重大的技术挑战,还可能引发死锁等一系列问题——而这些挑战,恰恰是所有大规模分布式系统与生俱来的难题。好在我们为 Workers 提供了诸如 Durable Objects 这样的强大工具,它能帮助开发者在大规模场景下,有效协调并管理共享的持久化状态。针对开发者在 Workers 中对文件系统的需求,我们基于 Workers 原本就具备的强大能力,构建出了理想的解决方案。

我们实施了一个虚拟文件系统,让您能够使用 node:fs API 来读写临时性的内存文件。这个虚拟文件系统是每个 Worker 独有的:在使用无状态 Worker 时,某个请求中创建的文件,在其他任何请求中都无法访问;但若使用 Durable Object,这个临时文件空间就能在多个用户的多次请求间共享。该文件系统目前是临时性的,也就是说,文件不会在 Worker 重启或重新部署后继续留存,所以它并不能替代 Durable Object Storage 机制,但它提供了一个强大的新工具,能大幅提升您使用 Durable Objects 的能力。

node:fs 模块提供了一组丰富的 API 来处理文件和目录:图6

该虚拟文件系统支持广泛的文件操作,包括读取和写入文件、创建和删除目录,以及处理文件描述符。它还支持通过 process.stdin、process.stdout 和 process.stderr 实现的标准输入/输出/错误流,同时也支持符号链接、流等更多功能。

虽然当前实现的虚拟文件系统仅支持内存存储,但我们正在探索未来添加持久化存储的方案,未来或将与 Cloudflare 现有的存储解决方案(如 R2 或 Durable Objects)实现联动。不过,您不必等待我们的更新!当结合 Durable Objects 和 JavaScript RPC 等强大工具时,您完全可以通过 SQLite 存储打造出属于自己的通用、持久化文件系统抽象层。

使用 node:crypto 进行加密

node:crypto 模块提供了一整套全面的加密功能,包括哈希、加密、解密等。我们已经完整实现了 node:crypto 模块,使您可以在 Workers 应用程序中使用熟悉的加密 API。由于 Workers 底层使用的是 BoringSSL,而 Node.js 使用的是 OpenSSL,因此与 Node.js 相比会存在一些行为上的差异。不过,我们已尽力使这些 API 尽可能兼容,目前许多依赖 node:crypto 的流行包都可以在 Workers 中无缝运行。

为实现这一目标,我们并未简单照搬 Node.js 中这些加密操作的实现方式。相反,我们深入 Node.js 项目内部,将核心加密功能提取出来,打造成了一个名为 ncrypto 的独立依赖项目。这个项目不仅供 Workers 使用,Bun 也能借助它,通过直接运行与 Node.js 完全相同的代码,来实现与 Node.js 兼容的功能。

图7

支持 node:crypto 模块的所有主要功能,包括:
哈希(例如,SHA-256、SHA-512)
HMAC
对称加密/解密
非对称加密/解密
数字签名
密钥生成和管理
随机字节生成
密钥推导函数(例如,PBKDF2、scrypt)
加密和解密数据流
签名和验证流
用于管理密钥的 KeyObject 类
证书处理(例如,X.509 证书)
支持各种编码格式(例如,PEM、DER、base64)。
以及更多……

进程与环境

在 Node.js 中,node:process 模块提供了一个全局对象,用于获取有关当前 Node.js 进程的信息并对该进程进行控制。它包含了一系列属性和方法,可用于访问环境变量、命令行参数、当前工作目录等。它是 Node.js 中最基础的模块之一,许多包都依赖它来实现基本功能,并默认其一定存在。然而,node:process 模块中的某些部分在 Workers 环境中是没有意义的,例如进程 ID 和用户/组 ID。这些概念与传统的服务器操作系统及进程模型紧密相关,而在 Workers 环境中并没有对应的机制。

当启用 nodejs_compat 标志时,您可以在 Worker 脚本中直接使用全局对象 process,或者通过 import process from 'node:process' 显式导入该模块。需要注意的是,process 全局对象仅在启用 nodejs_compat 标志时可用。若在未启用该标志的情况下尝试访问 process,其值将为 undefined,且导入操作将抛出错误。

让我们看一下在 Workers 中确实有意义并已完全实现的 process API,从 process.env 开始。

环境变量

Workers 很早就已经支持环境变量了,但在此之前,环境变量只能通过传递给 Worker 函数的 env 参数来访问。在 Worker 的顶层作用域中,是无法直接访问环境变量的:图8

通过全新的 process.env 实现,您现在可以像在 Node.js 中一样,以更熟悉的方式访问环境变量,并且可以在任意作用域(包括 Worker 的顶层作用域)中进行访问:图9

环境变量的设置方式与之前相同,可以通过 wrangler.toml 或 wrangler.jsonc 配置文件,也可以通过 Cloudflare 仪表板或 API 进行设置。它们既可以设置为简单的键值对,也可以设置为 JSON 对象: 图10

通过 process.env 访问时,所有环境变量值都是字符串,就像在 Node.js 中一样。

由于 process.env 可在全局范围内访问,因此需要注意的是,环境变量可在 Worker 脚本中的任何位置访问,包括您可能正在使用的第三方库。这与 Node.js 的行为一致,但从安全和配置管理的角度来看,这是一个需要注意的地方。Cloudflare Secrets Store 可以在 Workers 中提供增强的机密处理能力,作为使用环境变量的替代方案。

可导入环境和 waitUntil

当未启用 nodejs_compat 标志时,我们决定更进一步,使用户可以将环境变量和 waitUntil 机制作为模块导入,而不必总是通过传递给 Worker 函数的 env 和 ctx 参数来访问它们。这种方式可以让用户以更模块化的方式访问环境变量,也有助于避免在多层函数调用中反复传递 env 参数。这虽然不是 Node.js 兼容性方面的功能,但我们认为这是对 Workers 环境的一项实用增强。

图11

关于 process.env,有一个重要的注意事项:通过 process.env 对环境变量所做的更改,不会反映在传递给 Worker 函数的 env 参数中,反之亦然。process.env 是在 Worker 执行开始时填充的,并不会动态更新。这与 Node.js 的行为是一致的,在 Node.js 中,对 process.env 的修改也不会影响运行中进程的实际环境变量。我们之所以这样设计,是为了尽量降低第三方库(原本是为在 Node.js 中运行而编写的)无意中修改 Worker 其余代码所依赖的环境变量的风险。

Stdin、stdout、stderr

Worker 不像 Node.js 进程那样拥有传统的标准输入/输出/错误流。然而,我们实现了 process.stdin、process.stdout 和 process.stderr,它们作为类似流的对象,可以类似地使用。这些流并未连接到任何实际的进程标准输入和标准输出,但它们可用于捕获写入 Worker 日志的输出,其方式与 console.log 及其类似项相同,它们也会显示在 Workers 日志中。

process.stdout 和 process.stderr 是 Node.js 可写流:图12

对 stdin、stdout 和 stderr 的支持也已与虚拟文件系统集成,这让您能够使用 node:fs API 向标准文件描述符 0、1 和 2(分别对应 stdin、stdout 和 stderr)进行写入操作:图13

Cloudflare 保护整个企业网络,帮助客户高效构建互联网规模的应用程序,加速任何网站或互联网应用程序,抵御 DDoS 攻击,防止黑客入侵,并能协助您实现 Zero Trust 的部署与实施。

从任何设备访问 1.1.1.1,使用我们的免费应用加速和保护您的互联网。

立即联系我们,获取更多相关信息http://t.cn/A6Tx4yTT

#Cloudflare##Node.js#

发布于 北京