本文是关于 Wasm 组件模型、其生态系统及如何在 NGINX Unit 中使用 Wasm 组件的系列博文的第二篇。在第一篇中,我们介绍了所有概念部分。本文将重点介绍 Wasm 组件创建流程。
基于 Rust 的 Wasm 组件教程
Rust 是首选 WebAssembly 开发语言,在支持方面也最为成熟。在本示例中,我们将使用 Rust 及其生态系统创建一个可直接托管在 NGINX Unit 上的 Wasm 组件。
本教程适用于基于 Linux 的操作系统和 MacOS。如果您使用的是 Windows 系统,建议您使用 WSL2 (Windows Subsystem for Linux) 进行操作。如果您尚未安装 NGINX Unit 和 WebAssembly 语言模块,请参阅文档或使用官方 Docker 镜像 unit:wasm 进行安装。
Rust 开发设置
若无 Rust 生态系统,请先安装。截至本文撰写之时,Rust 1.76 是最新的稳定版本。如需安装 Rust,请参阅其网站上的说明。
安装完成后,可通过运行以下命令来确认 Rust 的当前版本:
$ rustc -V
rustc 1.76.0 (07dca489a 2024-02-04)
要创建 Wasm 组件,我们需要一些额外的工具。这是一次性设置,支持您编写 Rust 源代码并将其编译为 Wasm 组件。
添加 wasm32-wasi 编译器目标
wasm32-wasi 编译器目标将为 rustc 安装提供一般 Wasm 支持。运行以下命令添加目标:
$ rustup target add wasm32-wasi
安装 cargo-component
cargo-component 将添加一个 cargo 子命令,以便在 Rust 项目中构建 Wasm 组件,而无需任何中间步骤。若要安装最新版本,请运行以下命令:
$ cargo install cargo-component
安装 wasmtime 运行时和 CLI 进行测试
wasmtime-cli 将用于测试和使用 Wasm 组件。截至本文撰写之时,我们使用的是 Wasmtime 18。若要安装最新版本的 Wasmtime,请运行以下命令:
$ curl https://wasmtime.dev/install.sh -sSf | bash
有关 Wasmtime 及其安装的更多信息,请查看其 GitHub 代码库。
准备好所有工具后,我们就可以创建 Rust 项目了。
使用 wasi Rust 库
WASI Rust 官方库的使用体验很有意思。组件构建时间极短,而且库的依赖项占用空间非常小。不过,这在开发人员体验方面带来了一定的代价。亲自体验一下:
首先使用 cargo component 新建一个 Wasm 组件:
$ cargo component new --lib test-wasi-component
导航到 test-wasi-component 目录。
添加 wasi crate:
$ cargo add wasi
接下来,使用所选的文本编辑器修改 Cargo.toml 文件。将 proxy = true 配置添加到 [package.metadata.component] 部分。保存更改后,您的 Cargo.toml 文件应如下所示:
[package]
name = "test-wasi-component"
version = "0.1.0"
edition = "2021"
[dependencies]
bitflags = "2.4.2"
wit-bindgen-rt = "0.21.0"
wasi = "0.13.0"
[lib]
crate-type = ["cdylib"]
[package.metadata.component]
package = "component:test-wasi-component"
proxy = true
[package.metadata.component.dependencies]
src/lib.rs 中的实际代码应如下所示:
use wasi::http::types::{
Fields, IncomingRequest, OutgoingBody, OutgoingResponse, ResponseOutparam,
};
wasi::http::proxy::export!(Component);
struct Component;
impl wasi::exports::http::incoming_handler::Guest for Component {
fn handle(_request: IncomingRequest, response_out: ResponseOutparam) {
let hdrs = Fields::new();
let mesg = String::from("Hello, This is a Wasm Component using wasi/http:proxy!");
let _try = hdrs.set(&"Content-Type".to_string(), &[b"plain/text".to_vec()]);
let _try = hdrs.set(&"Content-Length".to_string(), &[mesg.len().to_string().as_bytes().to_vec()]);
let resp = OutgoingResponse::new(hdrs);
// 添加 HTTP 响应状态代码
resp.set_status_code(200).unwrap();
let body = resp.body().unwrap();
ResponseOutparam::set(response_out, Ok(resp));
let out = body.write().unwrap();
out.blocking_write_and_flush(mesg.as_bytes()).unwrap();
drop(out);
OutgoingBody::finish(body, None).unwrap();
}
}
要使用 wasi crate,我们需要做一些简单的 Rust 操作。这些操作并不麻烦,但您在选择此选项时需要稍加考虑。对于 wasi:http/proxy world,GitHub 上有一个接口说明,有助于您编写代码。
下面来构建组件。从 test-wasi-component 目录运行以下命令:
$ cargo component build --release
该 build 显示,依赖项占用空间非常小,这也是 wasi crate 的一大优势。
若要测试组件,我们可以使用 wasmtime serve。
$ wasmtime serve target/wasm32-wasi/release/test_wasi_component.wasm
输出结果应为:
$ wasmtime serve target/wasm32-wasi/release/test_wasi_component.wasm
Serving HTTP on http://0.0.0.0:8080/
向公开的端点发送请求,输出结果将如下所示:
$ curl -v localhost:8080
…
> GET / HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/8.4.0
> Accept: */*
>
< HTTP/1.1 200 OK
< content-type: plain/text
< content-length: 54
< date: Tue, 12 Mar 2024 12:28:56 GMT
<
* Connection #0 to host localhost left intact
Hello, This is a Wasm Component using wasi/http:proxy!
适用于生产级 Wasm 工作负载的 NGINX Unit
虽然 wasmtime-cli 接口非常适合在本地测试 Wasm 组件,但生产工作负载有着更高的要求。
有了 NGINX Unit Wasm 运行时,您就能在单个主机上运行 Wasm 工作负载及其他主机应用,并利用其他所有强大的 Unit 功能。考虑到 Unit 的设计,而且我们已将监听器与应用运行时解耦,您可在与 Wasm 组件共享请求或将 HTTPS 添加到堆栈之前,充分利用 Unit 路由器做出路由决策。
要在 NGINX Unit 上运行该组件,启动 Unit 并发送初始配置,确保使用绝对路径指向 Wasm 组件。
创建 config.json 文件:
{
"listeners": {
"127.0.0.1:8085": {
"pass": "applications/my-wasm-component"
}
},
"applications": {
"my-wasm-component": {
"type": "wasm-wasi-component",
"component": "path/target/wasm32-wasi/release/test_wasi_component.wasm"
}
}
}
使用 unitc 应用配置:
$ unitc config.json /config
向公开的端点发送请求,不同的运行时实现将输出相同的内容:
$ curl -v localhost:8085
…
< HTTP/1.1 200 OK
< content-type: plain/text
< content-length: 54
< Server: Unit/1.32.0
< Date: Tue, 12 Mar 2024 15:16:13 GMT
<
* Connection #0 to host localhost left intact
Hello, This is a Wasm Component using wasi/http:proxy!
这就是 Wasm 组件的优势所在。您只需构建一次,即可在所有运行时运行。
后续
Wasm 生态系统及其所有相关项目正快速成长和推进。每周都会推出新功能和新想法。NGINX Unit 对 Wasm 的支持从未改变,并将继续在我们的 Wasmtime 集成中实现新功能,同时发布有关 Wasm 的技术博文。
欢迎在我们的 GitHub 讨论页面上就此博文分享您的看法,并就 Wasm 组件使用方面的不足之处分享您的想法。