Cloudflare 宣布首次公开预览对 wasm32-unknown-emscripten 编译目标的实验性支持,该支持纳入 wasm-bindgen 工具链和 Rust Workers 环境。借助这一途径,可以将更多原生 Rust 应用和库构建并以 WebAssembly 形式运行在全球 Workers 平台上,而不再局限于那些预先兼容传统 WebAssembly 模型的库。
这项工作实现了 Emscripten 与 wasm-bindgen 的集成:Emscripten 提供将原生代码连接到 Web 环境的层,并模拟文件系统、计时器和套接字等功能;wasm-bindgen 则负责生成 Rust 与 JavaScript 之间的 bindings。根据新的变化,Rust 编译器和 Emscripten 编译器都可以在同一构建过程中使用对方的 bindings 层。
扩大 Rust 库兼容性
Cloudflare 表示,初步测试显示其与 Rust 库的兼容性有所提升,其中包括低级别库。libc、socket2 和 Mio 等库只需进行有限修改,即可将 Emscripten 目标加入平台条件。最复杂的库则是 Tokio,因为 Workers 在 JavaScript 事件循环中以单线程运行,而 Tokio 传统上依赖在线程等待输入输出操作时暂停线程。
为处理这一差异,Cloudflare 正在推进两条路径。第一条路径使用 WebAssembly JavaScript Promise Integration,使 WebAssembly 堆栈能够暂停并将控制权交还给 JavaScript 事件循环。这要求在堆栈暂停和恢复时精确管理线程局部上下文。第二条路径提出 Tokio 的 LocalEventLoop 模式:通过 drive() 操作执行已就绪的任务,并以通知宿主事件循环的信号取代等待。
套接字和 TCP 网络支持
网络支持是另一项挑战,因为 Tokio 通过 Mio 依赖 epoll_wait(),而 Emscripten 当时仅提供有限的 poll() 支持以及用于模拟 WebSocket 的层。因此,Cloudflare 向 Emscripten 提交了 40 多个合并请求,以增加 -sNODERAWSOCKETS 层;该层通过 node:net 接口提供 epoll、TCP、UDP 和 Unix sockets 支持。
由于 Workers 提供与 Node.js 接口的兼容性,同一桥接层可以在该平台内部使用,无需在 Emscripten 与 Cloudflare 之间额外增加独立的专用接口。这些能力仍处于实验阶段,Tokio 的部分补丁也仍在等待审核,之后才会最终合并到项目中。
通过 Minecraft 服务器进行实际测试
为测试兼容程度,Cloudflare 工程师 Dan Lapid 在 Durable Object 内运行了用 Rust 编写、名为 Pumpkin 的 Minecraft 服务器。由于 Durable Object 以单线程运行,游戏循环、区域调度和 Rayon 任务被转换为 Tokio 循环中的协作式任务,而不是依赖独立的操作系统线程。
服务器通过 Node.js 接口使用文件系统,并借助 worker-fs-mount 库将文件存储在 Durable Object 的 SQLite 中。玩家连接还会经过 TCP ingress,然后进入对象内部的 TCP 服务器。Cloudflare 表示,这使其能够运行支持多人游戏的 Minecraft 服务器,并保存状态,还能从已存储的世界中重新启动该对象。
为什么这项进展很重要?
这项变化的实际价值不仅在于增加一个新的编译目标,还在于减少为了在 Workers 上运行而需要重写的库和应用数量。不过,Cloudflare 将该支持描述为实验性且尚未发布,而 Tokio 的新设计、JSPI 支持以及部分 Emscripten 集成仍在开发和 upstream 审核中。因此,这一途径为尝试运行原生 Rust 应用打开了大门,但目前还不能证明它已经成为所有传统运行环境的完整且稳定替代方案。