通过 WasmEdge 嵌入WebAssembly 函数扩展 Golang 应用


本文摘自网络,作者,侵删。

GO 编程语言(Golang)是一种易于使用且安全的编程语言,可编译为高性能的原生应用程序。Golang 是编写软件基础设施和框架的非常流行的选择。

软件框架的一个关键要求是,用户能够使用自己的代码对其进行扩展和定制。但是,在 Golang 中,向现有应用程序添加用户定义的函数或扩展并不容易。通常,需要通过组合框架的源代码和用户定义的函数在源代码级别进行集成。虽然可以使用 Golang 创建动态共享模块,但这些广泛用于边缘计算的基于 ARM 的系统,缺乏对共享模块的支持。此外,源代码集成和动态模块都没有为用户定义的函数提供隔离。扩展可能会干扰框架本身,并且集成多方的用户定义函数会不安全。因此,Golang 作为“云原生”的语言,需要更好的扩展机制。

WebAssembly 提供了一种强大、灵活、安全且简单的扩展机制,可以将用户定义的函数嵌入到 Golang 应用程序中。最初为 Web 浏览器而发明,但越来越多地用于独立和服务器端应用程序,WebAssembly 是其字节码应用程序的轻量级软件容器。WebAssembly 是高性能、可移植的,并支持多种编程语言。

在本教程中,我们将讨论如何从 Golang 应用程序运行 WebAssembly 函数。 WebAssembly 函数是用 Rust 编写的。它们与 Golang 主机应用程序有着很好的隔离,同时函数之间彼此也进行了隔离。

wasm-go-rust-wasmedge.jpg

准备工作

显然,我们需要安装 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。

阅读剩余部分

相关阅读 >>

Go - 常用签名算法的基准测试

Golang 中const是什么

聊聊cortex的backoff

Go关于tpl文件无法读取域中数据{{$.name}}

Go语言从入门到实战,带你拿下Golang的高效编程法

2.Golang 操作elasticsearch-7

Golang elasticsearch7的使用

聊聊dubbo-Go-proxy的jtypes

Golang字符串国际化

Golang指针转字符串,Golang字符串转指针

更多相关阅读请进入《Go》频道 >>




打赏

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码打赏,您说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

分享从这里开始,精彩与您同在

评论

管理员已关闭评论功能...