GO 编程语言(Golang)是一种易于应用且平安的编程语言,可编译为高性能的原生应用程序。Golang 是编写软件基础设施和框架的十分风行的抉择。
软件框架的一个要害要求是,用户可能应用本人的代码对其进行扩大和定制。然而,在 Golang 中,向现有应用程序增加用户定义的函数或扩大并不容易。通常,须要通过组合框架的源代码和用户定义的函数在源代码级别进行集成。尽管能够应用 Golang 创立动静共享模块,但这些宽泛用于边缘计算的基于 ARM 的零碎,不足对共享模块的反对。此外,源代码集成和动静模块都没有为用户定义的函数提供隔离。扩大可能会烦扰框架自身,并且集成多方的用户定义函数会不平安。因而,Golang 作为“云原生”的语言,须要更好的扩大机制。
WebAssembly 提供了一种弱小、灵便、平安且简略的扩大机制,能够将用户定义的函数嵌入到 Golang 应用程序中。最后为 Web 浏览器而创造,但越来越多地用于独立和服务器端应用程序,WebAssembly 是其字节码应用程序的轻量级软件容器。WebAssembly 是高性能、可移植的,并反对多种编程语言。
在本教程中,咱们将探讨如何从 Golang 利用程序运行 WebAssembly 函数。WebAssembly 函数是用 Rust 编写的。它们与 Golang 主机应用程序有着很好的隔离,同时函数之间彼此也进行了隔离。
筹备工作
显然,咱们须要装置 Golang,这里假如你曾经装置了。
Golang 版本应该高于 1.15,咱们的示例能力工作。
下一步,请装置 WasmEdge 共享库。WasmEdge 是一个由 CNCF 托管的当先的 WebAssembly runtime。咱们将应用 WasmEdge 在 Golang 应用程序中嵌入和运行 WebAssembly 程序。
$ wget https://github.com/second-state/WasmEdge-go/releases/download/v0.8.1/install_wasmedge.sh
$ chmod +x ./install_wasmedge.sh
$ sudo ./install_wasmedge.sh /usr/local
最初,因为咱们的 demo WebAssembly 函数是用 Rust 编写的,因而您还须要装置 Rust 编译器和 rustwasmc 工具链。
嵌入一个函数
目前,咱们须要 Rust 编译器版本 1.50 或更低版本能力让 WebAssembly 函数与 WasmEdge 的 Golang API 一起应用。一旦 interface type 标准最终确定并失去反对,咱们会赶上最新的 Rust 编译器版本。
此示例中,咱们将演示如何从 Golang 应用程序调用一些简略的 WebAssembly 函数。这些函数是用 Rust 编写的,须要简单的调用参数和返回值。编译器工具须要 #[wasm_bindgen]
宏来主动生成正确的代码以将调用参数从 Golang 传到 WebAssembly。
WebAssembly 标准仅反对一些开箱即用的简略数据类型。Wasm 不反对例如字符串和数组的类型。为了将 Golang 中的丰盛类型传到 WebAssembly,编译器须要将它们转换为简略的整数。例如,它将字符串转换为整数内存地址和整数长度。嵌入在 rustwasmc 中的 wasm_bindgen 工具会主动进行这种转换。
use wasm_bindgen::prelude::*;
use num_integer::lcm;
use sha3::{Digest, Sha3_256, Keccak256};
#[wasm_bindgen]
pub fn say(s: &str) -> String {let r = String::from("hello");
return r + s;
}
#[wasm_bindgen]
pub fn obfusticate(s: String) -> String {(&s).chars().map(|c| {
match c {'A' ..= 'M' | 'a' ..= 'm' => ((c as u8) + 13) as char,
'N' ..= 'Z' | 'n' ..= 'z' => ((c as u8) - 13) as char,
_ => c
}
}).collect()}
#[wasm_bindgen]
pub fn lowest_common_multiple(a: i32, b: i32) -> i32 {let r = lcm(a, b);
return r;
}
#[wasm_bindgen]
pub fn sha3_digest(v: Vec<u8>) -> Vec<u8> {return Sha3_256::digest(&v).as_slice().to_vec();
}
#[wasm_bindgen]
pub fn keccak_digest(s: &[u8]) -> Vec<u8> {return Keccak256::digest(s).as_slice().to_vec();
}
首先,咱们应用 rustwasmc 工具将 Rust 源代码编译为 WebAssembly 字节码函数。请应用 Rust 1.50 或者更低版本。
$ rustup default 1.50.0
$ cd rust_bindgen_funcs
$ rustwasmc build
# The output WASM will be pkg/rust_bindgen_funcs_lib_bg.wasm
Golang 源代码 运行在 WasmEdge 中的 WebAssembly 函数的示例如下。ExecuteBindgen()
函数调用 WebAssembly 函数并应用 #[wasm_bindgen]
传入参数。
package main
import (
"fmt"
"os"
"github.com/second-state/WasmEdge-go/wasmedge"
)
func main() {/// Expected Args[0]: program name (./bindgen_funcs)
/// Expected Args[1]: wasm or wasm-so file (rust_bindgen_funcs_lib_bg.wasm))
wasmedge.SetLogErrorLevel()
var conf = wasmedge.NewConfigure(wasmedge.WASI)
var vm = wasmedge.NewVMWithConfig(conf)
var wasi = vm.GetImportObject(wasmedge.WASI)
wasi.InitWasi(os.Args[1:], /// The args
os.Environ(), /// The envs
[]string{".:."}, /// The mapping directories
[]string{}, /// The preopens will be empty
)
/// Instantiate wasm
vm.LoadWasmFile(os.Args[1])
vm.Validate()
vm.Instantiate()
/// Run bindgen functions
var res interface{}
var err error
res, err = vm.ExecuteBindgen("say", wasmedge.Bindgen_return_array, []byte("bindgen funcs test"))
if err == nil {fmt.Println("Run bindgen -- say:", string(res.([]byte)))
}
res, err = vm.ExecuteBindgen("obfusticate", wasmedge.Bindgen_return_array, []byte("A quick brown fox jumps over the lazy dog"))
if err == nil {fmt.Println("Run bindgen -- obfusticate:", string(res.([]byte)))
}
res, err = vm.ExecuteBindgen("lowest_common_multiple", wasmedge.Bindgen_return_i32, int32(123), int32(2))
if err == nil {fmt.Println("Run bindgen -- lowest_common_multiple:", res.(int32))
}
res, err = vm.ExecuteBindgen("sha3_digest", wasmedge.Bindgen_return_array, []byte("This is an important message"))
if err == nil {fmt.Println("Run bindgen -- sha3_digest:", res.([]byte))
}
res, err = vm.ExecuteBindgen("keccak_digest", wasmedge.Bindgen_return_array, []byte("This is an important message"))
if err == nil {fmt.Println("Run bindgen -- keccak_digest:", res.([]byte))
}
vm.Delete()
conf.Delete()}
接下来,让咱们应用 WasmEdge Golang SDK 构建 Golang 应用程序。
$ go get -u github.com/second-state/WasmEdge-go/wasmedge
$ go build
运行 Golang 应用程序,它将运行嵌入在 WasmEdge Runtime 中的 WebAssembly 函数。
$ ./bindgen_funcs rust_bindgen_funcs/pkg/rust_bindgen_funcs_lib_bg.wasm
Run bindgen -- say: hello bindgen funcs test
Run bindgen -- obfusticate: N dhvpx oebja sbk whzcf bire gur ynml qbt
Run bindgen -- lowest_common_multiple: 246
Run bindgen -- sha3_digest: [87 27 231 209 189 105 251 49 159 10 211 250 15 159 154 181 43 218 26 141 56 199 25 45 60 10 20 163 54 211 195 203]
Run bindgen -- keccak_digest: [126 194 241 200 151 116 227 33 216 99 159 22 107 3 177 169 216 191 114 156 174 193 32 159 246 228 245 133 52 75 55 27]
嵌入一整个程序
你能够应用最新的 Rust 编译器和
main.rs
创立一个独自的 WasmEdge 利用,而后将其嵌入一个 Golang 利用中。
除了函数,WasmEdge Golang SDK 也能够嵌入独立的 WebAssembly 应用程序,行将一个带有 main()
函数的 Rust 利用编译为 WebAssembly。
咱们的 demo Rust 应用程序 从一个文件中读取。留神这里不须要 #{wasm_bindgen]
,因为 WebAssembly 程序的输出和输入数据当初由 STDIN
和 STDOUT
传递。
use std::env;
use std::fs::File;
use std::io::{self, BufRead};
fn main() {
// Get the argv.
let args: Vec<String> = env::args().collect();
if args.len() <= 1 {println!("Rust: ERROR - No input file name.");
return;
}
// Open the file.
println!("Rust: Opening input file \"{}\"...", args[1]);
let file = match File::open(&args[1]) {Err(why) => {println!("Rust: ERROR - Open file \"{}\"failed: {}", args[1], why);
return;
},
Ok(file) => file,
};
// Read lines.
let reader = io::BufReader::new(file);
let mut texts:Vec<String> = Vec::new();
for line in reader.lines() {if let Ok(text) = line {texts.push(text);
}
}
println!("Rust: Read input file \"{}\"succeeded.", args[1]);
// Get stdin to print lines.
println!("Rust: Please input the line number to print the line of file.");
let stdin = io::stdin();
for line in stdin.lock().lines() {let input = line.unwrap();
match input.parse::<usize>() {Ok(n) => if n > 0 && n <= texts.len() {println!("{}", texts[n - 1]);
} else {println!("Rust: ERROR - Line \"{}\"is out of range.", n);
},
Err(e) => println!("Rust: ERROR - Input \"{}\"is not an integer: {}", input, e),
}
}
println!("Rust: Process end.");
}
应用 rustwasmc 工具将应用程序编译为 WebAssembly。
$ cd rust_readfile
$ rustwasmc build
# The output file will be pkg/rust_readfile.wasm
Golang 源代码运行在 WasmEdge 中 WebAssembly 函数,如下:
package main
import (
"os"
"github.com/second-state/WasmEdge-go/wasmedge"
)
func main() {wasmedge.SetLogErrorLevel()
var conf = wasmedge.NewConfigure(wasmedge.REFERENCE_TYPES)
conf.AddConfig(wasmedge.WASI)
var vm = wasmedge.NewVMWithConfig(conf)
var wasi = vm.GetImportObject(wasmedge.WASI)
wasi.InitWasi(os.Args[1:], /// The args
os.Environ(), /// The envs
[]string{".:."}, /// The mapping directories
[]string{}, /// The preopens will be empty
)
/// Instantiate wasm. _start refers to the main() function
vm.RunWasmFile(os.Args[1], "_start")
vm.Delete()
conf.Delete()}
接下来,让咱们应用 WasmEdge Golang SDK 构建 Golang 应用程序。
$ go get -u github.com/second-state/WasmEdge-go
$ go build
运行 Golang 利用。
$ ./read_file rust_readfile/pkg/rust_readfile.wasm file.txt
Rust: Opening input file "file.txt"...
Rust: Read input file "file.txt" succeeded.
Rust: Please input the line number to print the line of file.
# Input "5" and press Enter.
5
# The output will be the 5th line of `file.txt`:
abcDEF___!@#$%^
# To terminate the program, send the EOF (Ctrl + D).
^D
# The output will print the terminate message:
Rust: Process end.
接下来
本文中,咱们展现了在 Golang 应用程序中嵌入 WebAssembly 函数的两种办法:嵌入一个 WebAssembly 函数以及嵌入一个残缺的程序。更多示例能够参考 WasmEdge-go-examples GitHub repo。
下一篇文章,咱们将钻研将 AI 推理(图像识别)函数嵌入到基于 Golang 的实时流数据处理框架的残缺示例。这在智能工厂和汽车中有理论利用。