大家好,很高兴又见面了,我是"高级前端进阶",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发!
前言
本文主要和大家介绍2023年Go 生态最火的3个 WebAssembly 运行时!在年初,我也确实使用 WebAssembly 将客户端应用成功移植到了 Web,这也是为什么我一直对 WebAssembly 充满好奇的原因。我甚至在头条上开了一个合集《WebAssembly 前沿技术》来专门探讨 WebAssembly ,并将持续关注 WebAssembly 的最新动态。
下面是已发布部分文章传送门:
- 《2023 年让 WebAssembly 大火的 10+应用!》
- 《万字长文!2023 年 WebAssembly 各个运行时性能对比!》
- 《让 JavaScript 在 WebAssembly 上加速运行!》
正如大家所看到的,当我们还在迟疑是否要在日常开发中引入 WebAssembly 的时候,很多优秀的应用、工具已经开始吃 WebAssembly 的红利了,而且取得了不错的成就,这可能也是为什么各个浏览器厂商、开发者如此热衷 WebAssembly 的原因吧。
前几天已经重点介绍过与 JavaScript 生态相关的《全网最火的 5+优秀 WebAssembly 运行时》,今天带着大家细数那些在 Go 语言中优秀的 WebAssembly 运行时,希望大家在使用 Go开发的时候能真正用得上。话不多说,直接开始!
1.wasmer-go
1.1 什么是 wasmer-go
基于 Wasmer 的完整、成熟的 Go WebAssembly 运行时。主要特征如下:
- 易于使用:wasmer API 模仿标准的 WebAssembly API
- 快速:wasmer 尽可能快地执行 WebAssembly 模块,接近本机速度
- 安全:所有对 WebAssembly 的调用都将很快,但更重要的是,完全安全且在沙盒中。
wasmer-go 嵌入了编译为共享库对象的 Wasmer 运行时,因此可使用 CGO 来使用它。同时提供了一组预编译的共享库对象。因此,该库可在以下平台上运行(并经过测试):
1.2 使用 wasmer-go
可以通过如下命令快速安装 wasmer-go:
$ go get github.com/wasmerio/wasmer-go/wasmer
安装后即可在代码中引入 WebAssembly 模块:
// 这里是Go语言程序
package main
import (
"fmt"
"io/ioutil"
wasmer "github.com/wasmerio/wasmer-go/wasmer"
)
func main() {
wasmBytes, _ := ioutil.ReadFile("simple.wasm")
// simple.wasm表示已编译的 WebAssembly 二进制文件
engine := wasmer.NewEngine()
store := wasmer.NewStore(engine)
// 编译模块
module, _ := wasmer.NewModule(store, wasmBytes)
//实例化模块
importObject := wasmer.NewImportObject()
instance, _ := wasmer.NewInstance(module, importObject)
// 从 WebAssembly 实例获取“sum”导出函数。
sum, _ := instance.Exports.GetFunction("sum")
// 使用 Go 标准值调用导出函数。
// 推断 WebAssembly 类型并自动转换值。
result, _ := sum(5, 37)
fmt.Println(result)
// 输出 42!
}
然后执行如下命令即可:
go run simple.go
目前 wasmer-go 在 Github 上 2.4k 的 star、150+的 fork、超过 60+的项目使用它。
关于Wasmer的更多介绍可阅读我的另外一篇文章,即《全网最火的 5+优秀 WebAssembly 运行时》,参考资料已经在文末。
CGO: 是一个Go 语言自带的特殊工具,可以使用命令go tool cgo 来运行。 cgo可以用于生成能够调用C 语言代码的Go语言源文件,也就是说所有启用了CGO 特性的Go 代码,都会首先经过cgo 的预处理。
2.wazero
2.1 wazero
wazero 是用 Go 编写的符合 WebAssembly 核心规范 1.0 和 2.0 的运行时。 它具有零依赖性,并且不依赖于 CGO, 这意味着可以运行其他语言的应用程序并仍然保持交叉编译。导入 wazero 并使用以任何语言编写的代码扩展 Go 应用程序!
wazero 支持两种运行时配置,编译器已集成。 默认情况下,例如: wazero.NewRuntime(ctx)如果支持,则使用编译器,开发者也可以如下方式强制使用解释器:
r := wazero.NewRuntimeWithConfig(ctx, wazero.NewRuntimeConfigInterpreter())
wazero 运行时只要包括以下两个组成部分:
- 解释器 Interpreter: 是 Wasm 虚拟机的一个简单的基于解释器的实现。 它的实现没有任何平台(GOARCH、GOOS)特定代码,因此解释器可用于 Go 可用的任何编译目标(例如: riscv64)。
- 编译器 Compiler:在 Runtime.CompileModule 期间提前将 WebAssembly 模块编译成机器代码 (AOT), 这意味着 WebAssembly 函数在运行时本机执行。 编译器比解释器快,通常快一个数量级 (10x) 或更多, 其是在没有主机特定依赖性的情况下完成的。
2.2 wazero 使用
下面示例显示如何使用 WebAssembly 中定义的加法函数扩展 Go 应用程序。
$ go run add.go 7 9
7 + 9 = 16
wazero 是一个 WebAssembly 运行时,嵌入在主机应用程序中。 要运行 WebAssembly 函数,需要访问 WebAssembly 二进制文件 (Wasm),通常是一个 %.wasm 文件。
add.wasm 是使用 TinyGo 从 add.go 编译而来的,因为它是将 Go 源代码编译为 Wasm 的最常用方法,这是构建 %.wasm 二进制文件的最小命令。
cd testdata; tinygo build -o add.wasm -target=wasi add.go
目前 wazero 在 Github 上 2.7k 的 star、150+的 fork、超过 230+的项目使用它,代码贡献者超过 35+。
3.wasmtime-go
3.1 什么是 wasmtime-go
Go 生态的 Wasmtime 字节码联盟项目,wasmtime-go 使用 CGO 来使用用 Rust 编写的 Wasmtime 项目的 C API。 Wasmtime 的预编译二进制文件在标记版本上签入此存储库,因此开发者不必在本地安装 Wasmtime。
wasmtime-go 项目目前仅适用于 Linux x86*64、macOS x86_64 和 Windows x86_64。 在其他平台上构建将需要预先构建 Wasmtime 并使用 CGO** env vars 才能正确编译。wasmtime-go 项目已经通过 Go 1.13 或更高版本测试。
可以通过如下命令快速安装:
go get -u github.com/bytecodealliance/wasmtime-go/v7@v7.0.0
如果是 bazel 用户,需要将以下内容添加到 WORKSPACE 文件中:
go_repository(
(name = 'com_github_bytecodealliance_wasmtime_go'),
(importpath = 'github.com/bytecodealliance/wasmtime-go/v7'),
(version = 'v7.0.0')
);
3.2 使用 wasmtime-go
下面是使用 wasmtime-go 的 hello world 示例:
package main
import (
"fmt"
"github.com/bytecodealliance/wasmtime-go/v7"
)
func main() {
// wasmtime 中的几乎所有操作都需要一个上下文的“store”参数来共享,所以首先创建它
store := wasmtime.NewStore(wasmtime.NewEngine())
// 编译模块需要 WebAssembly 二进制输入
// 但是 wasmtime 包也支持将 WebAssembly 文本格式转换为二进制格式。
wasm, err := wasmtime.Wat2Wasm(`
(module
(import "" "hello" (func $hello))
(func (export "run")
(call $hello))
)
`)
check(err)
// 一旦有了二进制“wasm”,就可以将其编译成一个“*Module”
// 它代表已编译的 JIT 代码。
module, err := wasmtime.NewModule(store.Engine, wasm)
check(err)
// `hello.wat` 文件导入了一项,所以在这里创建该函数
item := wasmtime.WrapFunc(store, func() {
fmt.Println("Hello from Go!")
})
//接下来实例化一个模块,这是链接所有导入的地方。
// 有一个导入,所以需要在这里传递它。
instance, err := wasmtime.NewInstance(store, module, []wasmtime.AsExtern{item})
check(err)
// 在实例化之后,可以查找 run 函数并调用它
run := instance.GetFunc(store, "run")
if run == nil {
panic("not a function")
}
_, err = run.Call(store)
check(err)
}
func check(e error) {
if e != nil {
panic(e)
}
}
目前 wasmtime-go 在 Github 上 590+ 的 star、60+的 fork。
4.本文总结
本文主要和大家介绍Go 生态最火的3个 WebAssembly 运行时!因为篇幅有限,文章并没有过多展开,如果有兴趣,可以在我的主页继续阅读,同时文末的参考资料提供了大量优秀文档以供学习。最后,欢迎大家点赞、评论、转发、收藏!
参考资料
https://www.toutiao.com/article/7208714402630058531/
https://github.com/wasmerio/wasmer-go
https://github.com/tetratelabs/wazero/tree/main/examples/basic
https://github.com/tetratelabs/wazero
https://github.com/bytecodealliance/wasmtime-go
封面图文章参考地址:https://dev.to/taherfattahi/build-a-chat-service-using-golang-and-webassembly-part-1-1pee
封面图作者:Taher Fattahi的《Build a Chat service using GoLang and WebAssembly (part 1)》