WASI 0.3 发布
速览
WASI 0.3 版本正式发布。该版本继续推进 WebAssembly 在 Web 环境外的应用。
AI 深度解读
WASI 0.3 发布:WebAssembly 原生异步支持正式落地
背景
WebAssembly System Interface (WASI) 是 WebAssembly 在非浏览器环境中运行的标准接口规范。随着 WebAssembly Component Model(组件模型)的成熟,WASI 需要与其深度集成以提供更高效的系统调用能力。
此前发布的 WASI 0.2 版本在处理异步操作时存在显著局限:每个组件必须拥有独立的事件循环(event loop)或异步运行时。这意味着虽然组件可以在主机上运行,但不同组件的事件循环之间无法协调。如果某个组件使用流式或异步 API,它就无法与其他组件进行组合(composition)。这种架构限制了组件间的互操作性,使得构建复杂的、由多个组件构成的应用变得困难。
为了解决这一问题,WASI 子组(WASI Subgroup)投票批准了 WASI 0.3.0 规范,将其重新基于 WebAssembly Component Model 的原生异步原语。这一版本标志着 WASI 从“模拟异步”转向“原生异步”,是 WebAssembly 生态走向成熟的关键一步。
核心内容
WASI 0.3 的核心变革在于将异步功能直接嵌入到 Component Model 的规范中,从而彻底改变了组件处理 I/O 和并发的方式。
1. 从 wasi:io 到 Canonical ABI 的迁移
在 WASI 0.2 中,wasi:io 包负责处理可轮询对象(pollables)、输入流和输出流。而在 WASI 0.3 中,这些功能已迁移至 Component Model 的 Canonical ABI(标准 ABI)中,成为原生原语。
这一迁移带来了两个主要后果:
- 机械性简化:从 0.2 到 0.3 的大部分变更是机械性的,显著简化了之前的函数签名。
- 原生异步绑定:新的异步原语允许绑定生成器(bindings generators)为特定语言生成符合该语言习惯的异步绑定代码,提升了开发体验。
2. 组件模型异步 ABI 的架构革新
WASI 0.3 引入了 stream<T>、future<T> 和 async 作为 Canonical ABI 的一等公民(first-class constructs)。这与 WASI 0.2 有本质区别:
- 主机驱动的事件循环:在 WASI 0.3 中,主机(Host)负责管理所有组件共享的一个事件循环,而不是由每个组件自行管理。
- 所有权与调度:
stream<T>和future<T>类似于资源类型,是拥有句柄(owned handle)。跨组件边界传递时,所有权从调用者转移给被调用者。与资源类型不同,它们不能被借用(borrowed)。- 运行时负责调度。当值被交付给
future时,运行时会调度等待该值的任务,即使该值跨越了多个组件边界。 - 交付者可以是主机、另一个组件,甚至是持有读取端的同一组件。
- 基于完成(Completion-based)而非基于就绪(Readiness-based):
- 新的异步模型类似于 Linux 的
io_uring和 Windows 的 IOCP/IoRing API。 - 虽然这是一种基于完成的模型,但对于需要兼容性的程序,可以在其之上模拟
epoll/kqueue风格的就绪 API。
- 新的异步模型类似于 Linux 的
- 直接导出/导入异步函数:组件可以直接导出和导入
async func。WASI 0.2 中繁琐的start-foo/finish-foo/subscribe三步舞已不复存在。
3. WASI 接口与错误处理的改进
WASI 0.3 对接口进行了机械性重构,但解决了 WASI 0.2 中一个关键的流式处理问题。
- 流状态问题:在 WASI 0.2 中,终端错误会内联在每个流的
read调用中返回。如果调用者提前停止读取,将无法区分“流关闭”和“错误”。 - 解决方案:在 WASI 0.3 中,流现在返回一个额外的
future,该future独立于流的消费情况解析。这使得程序可以明确获知流的最终状态。
代码对比示例:
- WASI 0.2:
read-via-stream: func() -> result<input-stream, error-code>; - WASI 0.3:
read-via-stream: func() -> tuple<stream<u8>, future<result<_, error-code>>>;
4. 语言绑定的原生支持
Component Model 的一大优势是能够轻松生成其他语言的绑定。WASI 0.3 的原生异步支持使得绑定生成器可以生成感觉“原生”的异步代码。
- Rust 示例:使用
wit-bindgencrate,wasi:http/handler接口中的handle: async func可以直接映射为 Rust 的async fn,无需额外的回调或状态机管理。 - 多语言支持:Python、JavaScript、C# 和 C 等语言的异步支持正在推进中。这些语言通常依赖无栈协程(stackless coroutines)。
- Go 语言的特殊性:Go 运行时能够将看似同步的调用转换为异步调用,并通过虚拟线程(goroutines)提供并发执行。通过
componentize-go,开发者可以导出同步风格的Handle函数,运行时会在 ABI 边界暂停 goroutine,并在流就绪时恢复它,而不会阻塞程序的其他部分。
5. wasi:http 接口的重组
wasi:http 是变化最大的接口。除了机械性转换外,还重新组织了世界(worlds)和核心抽象。
- 两个新世界:
wasi:http/service:用于客户端调用和服务器端处理。wasi:http/middleware:作为service的超集,允许将传入的请求传递给另一个处理器。
- 服务链(Service Chaining):
middleware世界取代了 0.2 时代的proxy世界。- 关键创新在于支持组件之间的直接组合(direct composition)。微服务组件可以在同一进程内直接组合,而无需通过网络通信。
- 性能提升:对于大多数微服务场景,这能将调用其他微服务的时间从毫秒级降低到纳秒级,性能提升达六个数量级。
关键要点
- 规范稳定:WASI 0.3.0 已通过 WASI 子组投票,成为稳定版本。这意味着为当前版本编译的程序在未来(包括后续的 0.3.x 补丁版本)将保持兼容。
- 原生异步:异步功能不再是组件的负担,而是 Component Model Canonical ABI 的一部分。主机管理共享的事件循环,组件间可以高效协调。
- 简化开发:去除了 WASI 0.2 中复杂的异步协调机制(如手动订阅和状态管理),绑定生成器可以生成更符合语言习惯的异步代码。
- 流式处理增强:通过返回独立的
future解决流状态歧义,使错误处理和流关闭检测更加可靠。 - 微服务性能飞跃:
wasi:http的新架构支持进程内直接组件组合(Service Chaining),消除了网络开销,使微服务间调用速度提升约一百万倍(从毫秒到纳秒)。 - 运行时支持:Wasmtime 45 已运行最新候选版本,Wasmtime 46 将默认启用 Component Model Async 并正式支持 WASI 0.3.0。JavaScript 工具链
jco也在紧随其后提供支持。
意义与影响
WASI 0.3 的发布标志着 WebAssembly 从“沙箱中的脚本”向“系统级应用运行时”迈出了决定性的一步。
首先,异步原生的实现解决了 WebAssembly 互操作性的最大瓶颈。在 WASI 0.2
