<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Rust + Go 雙劍合璧:WebAssembly 領(lǐng)域應(yīng)用

          共 14900字,需瀏覽 30分鐘

           ·

          2021-07-17 02:27

          Go 語(yǔ)言是一種易于使用且安全的編程語(yǔ)言,可編譯為高性能的原生應(yīng)用程序。Golang 是編寫軟件基礎(chǔ)設(shè)施和框架的非常流行的選擇。

          軟件框架的一個(gè)關(guān)鍵要求是,用戶能夠使用自己的代碼對(duì)其進(jìn)行擴(kuò)展和定制。但是,在 Golang 中,向現(xiàn)有應(yīng)用程序添加用戶定義的函數(shù)或擴(kuò)展并不容易。通常,需要通過(guò)組合框架的源代碼和用戶定義的函數(shù)在源代碼級(jí)別進(jìn)行集成。雖然可以使用 Golang 創(chuàng)建動(dòng)態(tài)共享模塊,但這些廣泛用于邊緣計(jì)算的基于 ARM 的系統(tǒng),缺乏對(duì)共享模塊的支持。此外,源代碼集成和動(dòng)態(tài)模塊都沒(méi)有為用戶定義的函數(shù)提供隔離。擴(kuò)展可能會(huì)干擾框架本身,并且集成多方的用戶定義函數(shù)會(huì)不安全。因此,Golang 作為“云原生”的語(yǔ)言,需要更好的擴(kuò)展機(jī)制。

          WebAssembly 提供了一種強(qiáng)大、靈活、安全且簡(jiǎn)單的擴(kuò)展機(jī)制,可以將用戶定義的函數(shù)嵌入到 Golang 應(yīng)用程序中。最初為 Web 瀏覽器而發(fā)明,但越來(lái)越多地用于獨(dú)立和服務(wù)器端應(yīng)用程序,WebAssembly 是其字節(jié)碼應(yīng)用程序的輕量級(jí)軟件容器。WebAssembly 是高性能、可移植的,并支持多種編程語(yǔ)言。

          在本教程中,我們將討論如何從 Golang 應(yīng)用程序運(yùn)行 WebAssembly 函數(shù)。WebAssembly 函數(shù)是用 Rust 編寫的。它們與 Golang 主機(jī)應(yīng)用程序有著很好的隔離,同時(shí)函數(shù)之間彼此也進(jìn)行了隔離。

          GitHub 代碼:https://github.com/second-state/WasmEdge-go

          準(zhǔn)備工作

          顯然,我們需要安裝 Golang,這里假設(shè)你已經(jīng)安裝了。

          Golang 版本應(yīng)該高于 1.15,我們的示例才能工作。

          下一步,請(qǐng)安裝 WasmEdge 共享庫(kù)。WasmEdge 是由 CNCF 托管的領(lǐng)先的 WebAssembly runtime 。我們將使用 WasmEdge 在 Golang 應(yīng)用程序中嵌入和運(yùn)行 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 函數(shù)是用 Rust 編寫的,因此您還需要安裝 Rust 編譯器和 rustwasmc 工具鏈。

          嵌入一個(gè)函數(shù)

          目前,我們需要 Rust 編譯器版本 1.50 或更低版本才能讓 WebAssembly 函數(shù)與 WasmEdge 的 Golang API 一起使用。一旦 interface type 規(guī)范最終確定并得到支持,我們會(huì)趕上最新的Rust 編譯器版本 。

          此示例中,我們將演示如何從 Golang 應(yīng)用程序調(diào)用一些簡(jiǎn)單的 WebAssembly 函數(shù)。這些函數(shù)是用 Rust 編寫的,需要復(fù)雜的調(diào)用參數(shù)和返回值。編譯器工具需要 #[wasm_bindgen]宏來(lái)自動(dòng)生成正確的代碼以將調(diào)用參數(shù)從 Golang 傳到 WebAssembly。

          WebAssembly 規(guī)范僅支持一些開(kāi)箱即用的簡(jiǎn)單數(shù)據(jù)類型。Wasm 不支持例如字符串和數(shù)組的類型。為了將 Golang 中的豐富類型傳到 WebAssembly,編譯器需要將它們轉(zhuǎn)換為簡(jiǎn)單的整數(shù)。例如,它將字符串轉(zhuǎn)換為整數(shù)內(nèi)存地址和整數(shù)長(zhǎng)度。嵌入在 rustwasmc 中的 wasm_bindgen 工具會(huì)自動(dòng)進(jìn)行這種轉(zhuǎn)換。

          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 字節(jié)碼函數(shù)。請(qǐng)使用 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 源代碼 運(yùn)行在 WasmEdge 中的 WebAssembly 函數(shù)的示例如下。 ExecuteBindgen() 函數(shù)調(diào)用 WebAssembly 函數(shù)并使用 #[wasm_bindgen]傳入?yún)?shù)。

          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()
          }

          接下來(lái),讓我們使用 WasmEdge Golang SDK 構(gòu)建 Golang 應(yīng)用程序。

          $ go get -u github.com/second-state/WasmEdge-go/wasmedge
          $ go build

          運(yùn)行 Golang 應(yīng)用程序,它將運(yùn)行嵌入在 WasmEdge Runtime 中的 WebAssembly 函數(shù)。

          $ ./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]

          嵌入一整個(gè)程序

          你可以使用最新的 Rust 編譯器和 main.rs 創(chuàng)建一個(gè)單獨(dú)的 WasmEdge 應(yīng)用,然后將其嵌入一個(gè) Golang 應(yīng)用中。

          除了函數(shù), WasmEdge Golang SDK 也可以嵌入獨(dú)立的 WebAssembly 應(yīng)用程序,即將一個(gè)帶有  main() 函數(shù)的 Rust 應(yīng)用編譯為 WebAssembly。

          我們的demo Rust 應(yīng)用程序 從一個(gè)文件中讀取。注意這里不需要 #{wasm_bindgen] ,因?yàn)?WebAssembly 程序的輸入和輸出數(shù)據(jù)現(xiàn)在由 STDINSTDOUT 傳遞。

          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 工具將應(yīng)用程序編譯為 WebAssembly。

          cd rust_readfile
          $ rustwasmc build
          # The output file will be pkg/rust_readfile.wasm

          Golang 源代碼運(yùn)行在 WasmEdge 中 WebAssembly 函數(shù),如下:

          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()
          }

          接下來(lái),讓我們使用 WasmEdge Golang SDK 構(gòu)建 Golang 應(yīng)用程序。

          $ go get -u github.com/second-state/WasmEdge-go
          $ go build

          運(yùn)行 Golang 應(yīng)用。

          $ ./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.

          接下來(lái)

          本文中,我們展示了在 Golang 應(yīng)用程序中嵌入 WebAssembly 函數(shù)的兩種方法:嵌入一個(gè) WebAssembly 函數(shù)以及嵌入一個(gè)完整的程序。更多示例可以參考 WasmEdge-go-examples GitHub repo。

          GitHub 鏈接:https://github.com/second-state/WasmEdge-go-examples



          往期推薦


          我是 polarisxu,北大碩士畢業(yè),曾在 360 等知名互聯(lián)網(wǎng)公司工作,10多年技術(shù)研發(fā)與架構(gòu)經(jīng)驗(yàn)!2012 年接觸 Go 語(yǔ)言并創(chuàng)建了 Go 語(yǔ)言中文網(wǎng)!著有《Go語(yǔ)言編程之旅》、開(kāi)源圖書《Go語(yǔ)言標(biāo)準(zhǔn)庫(kù)》等。


          堅(jiān)持輸出技術(shù)(包括 Go、Rust 等技術(shù))、職場(chǎng)心得和創(chuàng)業(yè)感悟!歡迎關(guān)注「polarisxu」一起成長(zhǎng)!也歡迎加我微信好友交流:gopherstudio

          瀏覽 90
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  无码在线播放观看 | 日本无码久久嗯啊流水 | 大香蕉伊人综合 | 无码电影中文字幕 | 亚洲在线免费 |