9 个版本
0.5.0 | 2020 年 2 月 7 日 |
---|---|
0.4.0 | 2019 年 11 月 18 日 |
0.3.6 | 2019 年 11 月 7 日 |
0.3.2 | 2019 年 10 月 30 日 |
#819 在 WebAssembly
14KB
136 行
创建 WASM 浏览器服务工作者的正确方法。
此 crate 提供了 rust 库和 JS 粘合代码,允许将 POSIX 兼容的代码包装成 WASM/WASI 目标,以便在浏览器服务工作者中运行。它还提供了与主 Web 应用的输入/输出消息通道。
为什么选择 WASI?
WebAssembly 系统接口(WASI)是一个令人兴奋的新规范,它允许使用 WebAssembly 在任何地方安全、安全地运行 POSIX 类似的应用程序。 -> Medium / 使用 Wasmer-JS 在 JavaScript 中运行 WASI
编译到 WASI 的 POSIX 兼容应用程序现在也可以在浏览器中运行,考虑到代码重用和将服务器工作负载委托给客户端。此外,编译到 wasm32-wasi 目标代码的执行速度似乎比编译到其他具有 Web 绑定的 wasm32 目标代码快约 2 倍,因此 CPU 密集型工作负载现在可以以接近本地目标的性能执行。
我为什么需要 wasi-worker?
作为 Web 应用程序一部分执行的自定义 WASM 代码占用相同的 JavaScript 线程,因此如果 WASM 代码正在运行复杂的计算,它将在工作时阻止浏览器应用程序。为了使其在单独的线程中工作,我们可以使用 浏览器服务工作者。
如前所述,编译到 WASI 的代码似乎运行速度快约 2 倍(链接到基准测试)。唯一的问题是 WASI 并非专为从浏览器执行而构建,而是一个旨在在服务器端运行 WASM 代码的标准。利用 @wasmer/wasi,此 crate 提供了浏览器服务工作者 WASI 运行时以及与 Web 应用程序之间的通信桥梁。
使用示例
此示例需要 WASI JavaScript 绑定 它可以通过 wasi-worker-cli 部署 或 WASI 环境 以及正确预配置的文件系统
use wasi_worker::*;
struct MyWorker;
impl Handler for MyWorker {
fn on_message(&self, msg: &[u8]) -> std::io::Result<()> {
println!("My Worker got message: {:?}", msg);
Ok(())
}
}
fn main() {
// JS glue code will hook to /output.bin
ServiceWorker::initialize(ServiceOptions::default());
ServiceWorker::set_message_handler(Box::new(MyWorker {}));
// Send binary message to main browser application
ServiceWorker::post_message(b"message");
}
// Function will be called from JS on incoming message
#[no_mangle]
pub extern "C" fn message_ready() -> usize {
ServiceWorker::on_message()
.expect("ServiceWorker.on_message")
}
JavaScript WASI 绑定
JavaScript WASI 绑定建立在 @wasmer 库之上,可以使用 wasiworker 工具轻松部署
cargo install wasi-worker-cli
wasiworker install
将添加示例 worker
代码和 bin 目标到当前 crate 目录
wasiworker install
wasiworker deploy
将构建 worker
二进制目标,并在 ./dist
下使用 JS 粘合代码进行部署
wasiworker deploy
要修改 JS 粘合代码源代码,位于同一仓库。
更详细的示例
use wasi_worker::*;
struct MyWorker {}
impl Handler for MyWorker {
fn on_message(&self, msg: &[u8]) -> std::io::Result<()> {
// Process incoming message
println!("My Worker got message: {:?}", msg);
Ok(())
}
}
fn main() {
// In WASI setup output will go to /output.bin
#[cfg(target_os="wasi")]
let opt = ServiceOptions::default();
// In user filesystem we operate under current dir
#[cfg(not(target_os="wasi"))]
let opt = ServiceOptions {
output: FileOptions::File("./testdata/output.bin".to_string())
};
let output_file = match &opt.output {
FileOptions::File(path) => path.clone()
};
ServiceWorker::initialize(opt)
.expect("ServiceWorker::initialize");
// Attach Agent to ServiceWorker as message handler singleton
ServiceWorker::set_message_handler(Box::new(MyWorker {}));
// Send binary message to main browser application
// this requires JS glue see wasi-worker-cli
ServiceWorker::post_message(b"message")
.expect("ServiceWorker::post_message");
// It does not autodelete output file
std::fs::remove_file(output_file)
.expect("Remove output.bin");
}
待办事项
- 带有 WASI fs 接口的库代码
- 基本示例
- 文档
- worker 设置的 CLI 工具
- 退出时丢弃输出文件