WebAssembly 与 go

iyacontrol · · 564 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

Golang 在 1.11 版本中开始支持WebAssembly。

基本上目前有两种使用场景:

  • 浏览器端 -- 将golang 编写的程序编译成wasm,然后在浏览器中使用编译好的wasm。这样的意义在于,给了我们使用golang编写前端应用的能力,并且可以享受golang本身具备的诸如类型安全和协程的future。
  • 服务端 -- golang程序中使用其他语言编写的wasm模块。
截止到目前,golang并不能支持wasi,不过正在规划中。具体可以查阅 issue

下面我们通过两个demo分别演示一下这两种场景的使用姿势。

浏览器端

1:首先我们创建一个go工程hello-go,用来生成wasm模块。代码比较简单,如下:

package main

import (
    "fmt"
)

func main() {
    fmt.Println("Hello-go!")
}

然后我们开始使用如下命令编译,会生成一个lib.wasm文件:

GOARCH=wasm GOOS=js go build -o lib.wasm main.go

2:创建一个index.html 页面,该页面会使用到上一步编译好的lib.wasm

<!--
Copyright 2018 The Go Authors. All rights reserved.
Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file.
-->
<html>
  <head>
    <meta charset="utf-8" />
    <title>Go wasm</title>
  </head>

  <body>
    <script src="wasm_exec.js"></script>

    <script>
      if (!WebAssembly.instantiateStreaming) {
        // polyfill
        WebAssembly.instantiateStreaming = async (resp, importObject) => {
          const source = await (await resp).arrayBuffer();
          return await WebAssembly.instantiate(source, importObject);
        };
      }

      const go = new Go();

      let mod, inst;

      WebAssembly.instantiateStreaming(fetch("lib.wasm"), go.importObject).then(
        result => {
          mod = result.module;
          inst = result.instance;
          document.getElementById("runButton").disabled = false;
        }
      );

      async function run() {
        await go.run(inst);
        inst = await WebAssembly.instantiate(mod, go.importObject); // reset instance
      }
    </script>

    <button onClick="run();" id="runButton" disabled>Run</button>
  </body>
</html> 

大家可以看到使用了WebAssembly.instantiateStreaming方法加载wasm文件到浏览器页面中。

我们还需要从misc/wasm复制wasm_exec.js文件。

$ cp "$(go env GOROOT)/misc/wasm/wasm_exec.js" .

3: 我们需要有一个简单的基于net/http的文件服务器,以提供我们的index.html和其他各种WebAssembly文件:

package main

import (
    "flag"
    "log"
    "net/http"
)

var (
    listen = flag.String("listen", ":8080", "listen address")
    dir    = flag.String("dir", ".", "directory to serve")
)

func main() {
    flag.Parse()
    log.Printf("listening on %q...", *listen)
    log.Fatal(http.ListenAndServe(*listen, http.FileServer(http.Dir(*dir))))
}

启动该服务器后,当您导航至localhost:8080时,应该看到“run”按钮是可单击的,并且如果您在浏览器中打开控制台,则应该看到,每次单击该按钮,它都会打印出Hello-go!

本示例比较简单,实际上在go代码中可以操作dom,完成功能更加丰富的wasm模块。

服务端

在go代码中使用wasm,需要用到 wasmer-go -- 基于Wasmer的Go的完整且成熟的WebAssembly运行时。

该运行时性能非常强悍,具体性能测试数据如下:

要引用的wasm模块,为了方便,大家可以直接下载 [simple.wasm](https://link.zhihu.com/?target=https%3A//github.com/wasmerio/wasmer-go/blob/master/wasmer/test/testdata/examples/simple.wasm) 。该模块由rust语言编写,实现一个简单的两数求和的功能,具体代码逻辑如下:

#[no_mangle]
pub extern fn sum(x: i32, y: i32) -> i32 {
    x + y
}

接下来我们编写我们的golang程序,在程序中执行 simple.wasm 模块。

package main

import (
    "fmt"
    wasm "github.com/wasmerio/go-ext-wasm/wasmer"
)

func main() {
    // Reads the WebAssembly module as bytes.
    bytes, _ := wasm.ReadBytes("simple.wasm")
    
    // Instantiates the WebAssembly module.
    instance, _ := wasm.NewInstance(bytes)
    defer instance.Close()

    // Gets the `sum` exported function from the WebAssembly instance.
    sum := instance.Exports["sum"]

    // Calls that exported function with Go standard values. The WebAssembly
    // types are inferred and values are casted automatically.
    result, _ := sum(5, 37)

    fmt.Println(result) // 42!
}

运行我们的代码:

$ go run main.go                             
go: finding module for package github.com/wasmerio/go-ext-wasm/wasmer
go: found github.com/wasmerio/go-ext-wasm/wasmer in github.com/wasmerio/go-ext-wasm v0.3.1
# command-line-arguments
clang: warning: argument unused during compilation: '-nopie' [-Wunused-command-line-argument]
42

总结

本文WebAssembly和go两种使用场景,并通过两个简单的demo,试图让大家理解这两种场景和使用方法。


有疑问加站长微信联系(非本文作者)

本文来自:Segmentfault

感谢作者:iyacontrol

查看原文:WebAssembly 与 go

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

564 次点击  
加入收藏 微博
上一篇:GO
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传