一分鐘搞明白!快速掌握 Go WebAssembly

大家好,我是煎魚。

最近因爲各種奇怪的原因,更多的接觸到了 WebAssembly。雖然之前很多博客也翻過寫過各種文章,但總感覺欠些味道。於是今天梳理了一版,和大家一起展開學習。

先來一張經典圖:

WebAssembly 是什麼

以下是 Mozilla 在 MDN 上給出的定義:

WebAssembly(縮寫:Wasm)是一種新的編碼方式,可以在現代的網絡瀏覽器中運行 - 它是一種低級的類彙編語言,具有緊湊的二進制格式,可以接近原生的性能運行,併爲諸如 C/C++ 等語言提供一個編譯目標,以便它們可以在 Web 上運行。它也被設計爲可以與 JavaScript 共存,允許兩者一起工作。

Wasm 官網自己挑出的重點是:

新編碼方式,可以在瀏覽器中運行。可以以接近原生的性能運行。可以跨語言,例如:C/C++;可以與 JavaScript 共存。

看着是一個不錯的可跨平臺運行的新玩具。

由哪家研發

最早 WebAssembly1.0 基於 asm.js(Javascript 的嚴格子集,靜態類型,取消垃圾回收機制等)的特性集實現。隨後的 WebAssembly2.0 又根據新的標準進行了進一步的延伸和開發。

WebAssembly 的開發團隊分別來自 Mozilla、Google、Microsoft、Apple,代表着四大網絡瀏覽器 Firefox、Chrome、Microsoft Edge、Safari。

一些重要的時間線:

幾家大廠派人一起做的,比較新,近幾年纔開始更多的被支持。當前還在 WebAssembly 2.0 的階段,還在發展階段。

1.0 和 2.0 區別之一

直觀上來看,1.0 和 2.0 最大的區別在於:

WebAssembly 2.0 起,邊界更大了,目標完全就是可移植、安全的高級語言。期望應用於瀏覽器、各編程語言、各系統中。

大有一個 JVM 的感覺:

Go 快速上手

在我們快速瞭解了 WebAssembly 的背景後。我們落到實處,看看 Go 語言的 WebAssembly 情況如何,又是如何使用。

Go 目前有兩種使用 WebAssembly 的方式,第一種是使用 syscall/js 標準庫,四捨五入算勉強支持了 WebAssembly 1.0。

代碼如下:

package main

import "syscall/js"

func main() {
 alert := js.Global().Get("alert")
 alert.Invoke("腦子進煎魚了!")
}

編譯命令:

GOOS=js GOARCH=wasm go build -o jianyu.wasm

再使用 JS 中對應 WebAssembly 的調用就可以了。

第二種使用方式,是使用開源庫 tinygo-org/tinygo[1],其基於 LLM,支持了 WebAssembly1.0/2.0 (WASM/WASI)。

tinygo brew 安裝方式:

$ brew tap tinygo-org/tools
$ brew install tinygo

如果安裝成功,執行 tinygo version 就可以看到版本信息。

Go wasm 代碼如下:

import (
 figure "github.com/common-nighthawk/go-figure"
)

//export HelloWorld
func HelloWorld() {
 myFigure := figure.NewFigure("Hello World"""true)
 myFigure.Print()
}

func main() {}

編譯命令:

tinygo build -o module.wasm -target wasi .

運行成功後,將會在對應目錄編譯出 module.wasm 二進制文件,可以由其他的平臺、語言進行使用。

如果你希望在 Go 或其他語言中調用所生成的 .wasm,需要找到對應符合 WASI 的庫和規則。

下面是 Go wasmer-go 調用 .wasm 的代碼:

import (
 "fmt"
 "io/ioutil"

 wasmer "github.com/wasmerio/wasmer-go/wasmer"
)

func main() {
 wasmBytes, _ := ioutil.ReadFile("module.wasm")

 store := wasmer.NewStore(wasmer.NewEngine())
 module, _ := wasmer.NewModule(store, wasmBytes)

 wasiEnv, _ := wasmer.NewWasiStateBuilder("wasi-program").
  // Choose according to your actual situation
  // Argument("--foo").
  // Environment("ABC""DEF").
  // MapDirectory("./"".").
  Finalize()
 importObject, err := wasiEnv.GenerateImportObject(store, module)
 check(err)

 instance, err := wasmer.NewInstance(module, importObject)
 check(err)

 start, err := instance.Exports.GetWasiStartFunction()
 check(err)
 start()

 HelloWorld, err := instance.Exports.GetFunction("HelloWorld")
 check(err)
 result, _ := HelloWorld()
 fmt.Println(result)
}

func check(e error) {
 if e != nil {
  panic(e)
 }
}

運行成功後會輸出 “Hello World”。

Go 標準庫 syscall/js 標準庫只支持 WebAssembly1.0,只能應用於 JS 相關的場景,並不能被其他語言所集成。

其中 tinygo 實現了 WASI,藉助 WASI 這一標準接口與其他平臺語言互相集成。但 tinygo 並沒有支持所有的 Go 語法特性,具體可以查看 Go language features[2]。

這塊需要特別注意,不同語言對 WebAssembly(WASI)的支持程度均不一樣。

其他應用場景

帶火 WebAssembly 的還有一個重要的因素,那就是在各種雲原生的組件上都可以集成和使用,進一步延伸了場景。

例如在 Envoy 和 Istio 上,可以使用 wasm 很方便的將自定義 filter 集成到 Envoy 中,實現 Envoy 代理的功能增強。

總結

今天我們快速的對 WebAssembly 進行了背景瞭解、Go 快速上手、擴展場景瞭解等。雖然 WebAssembly 是一個新輪子,也號稱可以藉助 WASI 集成和被集成。

但實際上現在各語言對 WebAssembly 的支持程度都不一樣,像 Go 官方自己提供的標準庫就維護的不怎麼樣,WASI 的 issues 也沒有繼續推進。

甚至在其他語言的互通,現在仍然有着或多或少會導致阻塞無法應用的問題。可能,還需要再過個 3~5 年?

但如此多語言的庫維護,能否長久的迭代和維護。也是一個更大的問題。

參考資料

[1]

tinygo-org/tinygo: https://github.com/tinygo-org/tinygo

[2]

Go language features: https://tinygo.org/docs/reference/lang-support/

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/NLMOGkhCkE3X1QXByW8f4Q