前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >使用 Docker 和 Golang 快速上手 WebAssembly

使用 Docker 和 Golang 快速上手 WebAssembly

作者头像
soulteary
发布2021-11-25 10:20:00
1.1K0
发布2021-11-25 10:20:00
举报
文章被收录于专栏:为了不折腾而去折腾的那些事

本文将聊聊,如何使用 Docker 和 Golang 快速上手 WebAssembly。我会分别从浏览器场景和“通用应用”场景来进行叙述,如果你还徘徊在 WebAssembly 的门前,或许这篇文章会对你所有帮助。

写在前面

如果从 2017 年浏览器纷纷开始以实验性的方式,支持 Web WebAssembly 功能来看,在浏览器使用非 JavaScript 来完成计算的风已经吹了五年了。不过,感受到 Wasm 生态真正发力的是近三年。

大环境的变化,让行业生态中音视频、云计算、物联网有了更广阔的市场,以及在降本提效上更高的追求,此为天时。如果说 Wasm 生态中的 C 位是 Mozilla,那么去年在 Mozilla 裁员事件出现后,他们迅速成立 Rust 的基金会,以保障 Rust 开发团队能够独立、稳定地运行,保护 Rust 以及周边项目的持续发展,为生态提供土壤,此可谓地利。

天时地利,只待人和。

国内外经济环境均有了前所未有的变化,在少了不少外部资本诱惑之后,能够感受到这几年来,基础技术设施的蓬勃发展,这里面少不了各种优秀的工程师正在将注意力从“业务”,逐步转移到“技术”上。目前 Wasm 王国在它的一等公民 Rust 高速发展和推动下,已经吸引了不少其他语言生态、知名商业公司的注意力。至于何时爆发,我个人认为,只是时间问题。

不过需要注意的是,**没有技术会是银弹,只有把技术放在适用的场景下才能达到事半功倍的效果。**那么哪些场景适合 WebAssembly 呢?

为了行文方便,接下来 WebAssembly 会简称为 Wasm

适用场景 & 优势

先来看看,近三年业界公开表明已使用它的场景:

  • 做在线设计工具的业务场景,比如 Figma:早在 2017 年,Figma 就借助这项技术进行了产品优化,《WebAssembly cut Figma's load time by 3x》,他们的工程师 Rasmus Andersson 也从比较底层的角度分析了 wasm。
  • 复杂的在线 IDE 产品,比如 vim.wasm:在 2019 开始正式进行开发的 基于 wasm 的 VIM 完整移植版。使用 Web Worker 和 SharedArrayBuffer 解决了在浏览器端 JS 和 wasm 进行数据交互时的延迟问题。
  • 云计算场景的边缘计算,比如 Cloudflare、fastly :《WebAssembly on Cloudflare Workers》、《Compute@Edge》
  • 云计算场景网关能力扩展,比如 Envoy & istio、蚂蚁金服、MegaEase、ApiSix:《Istio1.5 & Envoy 数据面 WASM 实践》、《WebAssembly 在 MOSN 中的实践 - 基础框架篇》 、《用 Easegress + WebAssembly 做秒杀》、《云原生网关 APISIX 核心流程源码分析与进化方向思考》
  • 在线音视频处理,比如 Zoom、声网 agora、字节跳动:《Zoom on Web: WebAssembly SIMD, WebTransport, and WebCodecs》、《How WebRTC & WASM are opening new opportunities for web apps》、《如何通过 WebAssembly 在 Web 进行实时视频人像分割》、《Bilibili - WebAssembly在多媒体场景的实践与思考》
  • 高性能的复杂在线数据可视化,比如 perspective:https://github.com/finos/perspective
  • 浏览器端的前端加密场景,比如coupang :《WebAssembly 在性能及加密场景的深度探索》

如果将上面的场景进行归纳,我们可以看到,在浏览器端、云计算、嵌入式方向,WebAssembly 的优势还是比较大的:

  • 用比较低的代码量来扩展现有业务能力(云端、浏览器端)
  • 充分使用客户端的计算能力,节约云服务器带宽和计算资源成本(浏览器端)
  • 利用 wasm 高性能计算方面带来的优势,解决复杂的计算的执行效率问题(可视化、通用计算场景)
  • 快速复用其他语言技术栈道能力,借助容器化的思路快速迭代产品(云端、浏览器端、嵌入式)
  • 使用更流行、易于开发维护,或者贴合自己团队的语言来进行产品迭代(嵌入式)
  • 前端敏感内容的加密处理(浏览器端)

简单起步:浏览器中的 WebAssembly

循序渐进,我们先从最简单的场景开始:浏览器。

环境准备

如果你不想折腾 golang 的本地开发环境,我们可以使用 Docker 来快速创建一个运行环境:

代码语言:javascript
复制
docker run --rm -it -v `pwd`/code:/app -p 8012:8012 golang:1.17.3-buster bash

这里,我们将本地的 code 目录,映射到容器内的 /app 目录中,并将本地和容器中的 8012 端口打通,以备后续使用。

接着,在命令执行完毕后的容器的终端控制台中进行项目的初始化:

代码语言:javascript
复制
cd /app
go mod init soulteary.com/wasm-demo/v2

然后,使用你喜欢的方式(在容器内或者在本地 IDE中),创建一个 golang 的程序文件,比如 main.go

代码语言:javascript
复制
package main

import "fmt"

func main() {
 fmt.Println("一切都将从这里开始")
}

完成之后,在容器控制台内执行 go run main.go,不出意外,将看到 “一切都将从这里开始”的文本输出结果。

因为我们要演示的场景包含前端,所以还需要有一个简单的 Web 服务器,继续使用 golang 写一个简单的 Web 服务器吧。

代码语言:javascript
复制
package main

import (
 "log"
 "net/http"
)

func main() {
 log.Fatal(http.ListenAndServe(":8012", http.FileServer(http.Dir("."))))
}

将上面的内存保存为 server.go,当我们执行它的时候,它会将本地作为服务器根目录,对访问者提供 Web 服务。

在这个场景下,工程师们一般会有几个问题:

  • 如何得到一个 Wasm 程序
  • 如何将这个程序放在浏览器中运行;
  • 如何让浏览器中的 JavaScript 能够调用 WASM 的导出函数。( Golang 程序中的函数)
  • 以及如何针对整个程序进行进一步性能优化

在 “Show You The Code” 的过程中,我们将依次解答上面的问题。

从 Golang 创建 WebAssembly 程序

将 Golang 程序“变成” WebAssembly 一般会采取两种方案:

  • 使用 Golang 原生编译器进行编译。
  • 使用 TinyGo 编译器进行编译。

Golang “原生编译器方案”适用性非常好,适合项目初期开发、或者不太介意编译产物尺寸、程序首次分发时间的 B 端产品使用,如果你愿意投入时间做产物体积裁剪,也能够获得不错的结果。构建命令一般会类似 GOOS=js GOARCH=wasm go build -o YOUR_MODULE_NAME.wasm .,构建产物需要配合 Golang wasm_exec.js 使用。

相比较前者,TinyGo 的编译结果更小巧,可以用于嵌入式场景(官方目前支持60多种单片机)、支持 WASI 接口的云计算场景,以及本文本小节提到的 Web 场景。经过 GZip 压缩后,你的程序甚至不如一张图片大。构建命令和原生类似 tinygo build --no-debug -o YOUR_MODULE_NAME.wasm -target wasi .,不同的是,除了支持构建结果为 wasm 之外,支持沟通通用的 wasi ,方便你进行多端功能复用。(这个能力在分发模式上类似 Docker、在应用角度来看,则有些类似 Node 刚出现时,我在淘宝团队实践的前后端代码复用。)

我们先以原生方式为例,基于“环境准备”小节中的内容,使用下面的命令就能够完成对 wasm 的编译啦:

代码语言:javascript
复制
GOOS=js GOARCH=wasm go build -o module.wasm main.go

接着,将 Golang 提供的 “JS Bridge” 复制到项目根目录。

代码语言:javascript
复制
cp "$(go env GOROOT)/misc/wasm/wasm_exec.js" .

然后,编写一个落地页,让它能够加载上面的 JS Bridge,自动下载我们编译好的 wasm 程序,在程序下载完成后自动执行:

代码语言:javascript
复制
<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("module.wasm"), go.importObject).then(
      async (result) => {
        mod = result.module;
        inst = result.instance;

        await go.run(inst);
        inst = await WebAssembly.instantiate(mod, go.importObject); // reset instance
      }
    );

  </script>
</body>
</html>

一切就绪后,我们执行 go run server.go。在浏览器中访问 localhost:8012 就能够看到控制台中输出了上文中久违的字符串:“一切都将从这里开始”。

浏览器中出现了 Wasm 输出的内容

创建可与 JS 交互的 API 接口

我们以一个基础的 MD5 计算为例展开本小节的故事:假设我们需要让浏览器中的 JavaScript 调用 Golang 中的 MD5 计算函数。

先对“环境准备”小节中的“main.go” 文件进行调整,完成基础计算部分。

代码语言:javascript
复制
package main

import (
 "crypto/md5"
 "fmt"
)

func main() {
 fmt.Println("一切都将从这里开始")
 fmt.Println(CalcMd5("想要计算的结果"))
}

func CalcMd5(src string) string {
 return fmt.Sprintf("%x", md5.Sum([]byte(src)))
}

使用 go run main.go 运行程序,可以看到类似下面的结果:

代码语言:javascript
复制
一切都将从这里开始
849d1b972ec01975a9d1e16f804fec94

接着,将上面的程序进行语法调整,将新增的函数 CalcMd5 声明为 JS 可访问的Wasm 导出函数。

代码语言:javascript
复制
package main

import (
 "crypto/md5"
 "fmt"
 "syscall/js"
)

func main() {
 fmt.Println("一切都将从这里开始")

 wait := make(chan struct{}, 0)
 js.Global().Set("CalcMd5", js.FuncOf(CalcMd5))
 <-wait
}

func CalcMd5(this js.Value, p []js.Value) interface{} {
 ret := fmt.Sprintf("%x", md5.Sum([]byte(p[0].String())))
 return js.ValueOf(ret)
}

执行 GOOS=js GOARCH=wasm go build -o module.wasm main.go 对模块进行编译构建。然后再次执行 go run server.go,在浏览器中的控制台中,我们就能够通过 JS 调用刚刚在 Golang 中创建的计算 MD5 的函数 CalcMd5 了。

在浏览器中调用 Wasm 函数

如果你的项目没有使用 cgo ,那么可以考虑直接使用 TinyGo 进行编译器替换,编译的默认产物将缩小到一个让你惊讶的尺寸。(TinyGo 的代码示例,关于 TinyGo 的讨论,下文中有详细展开,再次不做更多描述)

想要使用 TinyGo,需要先调整之前的 JS Bridge 为 TinyGo 的版本。

代码语言:javascript
复制
cp "$(tinygo env TINYGOROOT)/targets/wasm_exec.js" .

继续使用 tinygo build --no-debug -o module.tiny.wasm -target wasm main.go 构建小巧的 wasm 程序即可。程序构建完毕,我们对照一下原生构建的文件的尺寸变化,可以看到优化结果非常明显,甚至进一步压缩之后,文件尺寸大小只有 64kb 左右:

代码语言:javascript
复制
du -hs *
148K module.tiny.wasm
2.0M module.wasm
// 64K module.tiny.wasm.gz

当然,使用 Go 创建的程序,并不单单是创建让 JS 调用的接口,还能够在 Go 中调用浏览器环境中的 JS API,或者在 Go 中直接操作浏览器 BOM API,来改变整个浏览器中页面的呈现和行为。

TinyGo 异常报错修复

在浏览器控制台中使用 TinyGo 版本的程序,可能会出现一些异常报错,比如会收到:“syscall/js.finalizeRef not implemented” 这类报错,解决方案可以参考 GitHub 中的方案,对 wasm_exec.js 文件打个补丁。

进阶操作:拿 WebAssembly 当容器使用,构建通用 WASI 程序

在最近十年里,不少语言都曾提出了“write once,run anywhere”的宏伟目标,其中 Node.js 更是使用语言同构的思路进行了践行。然而在容器时代,我们发现,异构的技术栈也很香啊,只要你的应用接口能够标准化、通讯效率足够高、计算过程中损失成本小就行了。

我们可以将现有的容器技术视作带有 OS 、程序运行依赖的,高纬度的轻量应用运行环境。而 WASI 应用,则是粒度更细的“灵活容器”:它可以被任何环境中、非常多的语言集成使用,在执行过程中被以更 low-level 的方式解析执行,或者作为轻量的沙盒使用。 如果说以前我们将程序扔在容器里,用显示声明的方式来实现“code as infrastructure”,那么 Wasm/ WASI 的到来,则让我们的程序本身具备了组件容器化的能力,尤其是针对跨栈、异构场景的能力扩展。

环境准备

为了体验 Wasm 程序的“通用性”,我们将编写一个 Wasm 程序,并使用 浏览器、Node、Golang 三种不同的运行环境来对其进行调用。为了能够快速的开发和验证,我这里准备了一个简单的容器环境:

代码语言:javascript
复制
FROM golang:1.17.3-buster
# 前置准备
RUN sed -i -e "s/deb\.debian\.org/mirrors\.tuna\.tsinghua\.edu\.cn/" /etc/apt/sources.list && \
    sed -i -e "s/security\.debian\.org/mirrors\.tuna\.tsinghua\.edu\.cn/" /etc/apt/sources.list && \
    apt-get update && apt-get install -y && \
    rm /bin/sh && ln -s /bin/bash /bin/sh && \
    echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections
# 准备 Go 环境
RUN go env -w GO111MODULE=on
RUN go env -w  GOPROXY=https://goproxy.cn,direct
RUN curl -L https://github.com/tinygo-org/tinygo/releases/download/v0.21.0/tinygo_0.21.0_amd64.deb -o tinygo_0.21.0_amd64.deb && \
    dpkg -i tinygo_0.21.0_amd64.deb
# 安装 wasmer
RUN curl https://get.wasmer.io -sSfL | sh
# 准备 Node 环境
RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
ENV NVM_DIR /root/.nvm
ENV NVM_NODEJS_ORG_MIRROR "https://npm.taobao.org/mirrors/node"
RUN . $NVM_DIR/nvm.sh && nvm install node

WORKDIR /app

将上面的内容保存为 Dockerfile,然后使用 docker build -it wasm-dev-env . 执行构建。考虑到使用时的简单,我们还可以编写一个容器编排配置:

代码语言:javascript
复制
version: '3'

services:

  wasm-dev-env:
    image: wasm-dev-env
    volumes:
      - ./app:/app
    command: tail -f /etc/hosts
    ports: 
      - 8081:8081
      - 8082:8082
      - 8083:8083

将上面的内容保存为 docker-compose.yml ,然后使用 docker-compose up -d 启动容器,容器启动后,使用 docker-compose exec wasm-dev-env bash 就能够进入我们的开发环境了,先来看一下各个组件的版本。

代码语言:javascript
复制
# go version
go version go1.17.3 linux/amd64

# node --version
v17.1.0

# wasmer --version
wasmer 2.0.0

#tinygo version
tinygo version 0.21.0 linux/amd64 (using go version go1.17.3 and LLVM version 11.0.0)

编写通用的 WASI 程序

考虑到实用性和趣味性,这里我将会把一个开源的 Go 软件编译成 WASI 程序,来让其他的语言的程序进行调用。我选择的开源项目是能够将普通的文本转换为 ASCII ART 的 https://github.com/common-nighthawk/go-figure

先来初始化项目目录。

代码语言:javascript
复制
mkdir /app/wasm
cd /app/wasm
go mod init soulteary.com/wasm-demo/v2

接着,创建一个名为 funny.go 的程序:

代码语言:javascript
复制
package main

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

func main() {}

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

然后使用 tinygo build --no-debug -o module.wasm -wasm-abi=generic -target=wasi funny.go 进行程序编译。不过由于 TinyGo 目前的 fs 模块的兼容性问题,我们的编译会失败:

代码语言:javascript
复制
# github.com/common-nighthawk/go-figure
../../go/pkg/mod/github.com/common-nighthawk/go-figure@v0.0.0-20210622060536-734e95fb86be/bindata.go:3606:11: MkdirAll not declared by package os
../../go/pkg/mod/github.com/common-nighthawk/go-figure@v0.0.0-20210622060536-734e95fb86be/bindata.go:3614:11: Chtimes not declared by package os

考虑到我们可以不需要原始项目中自定义外部资源的能力,所以可以直接针对报错的依赖文件进行调整,删除 TinyGo 中不支持的 API 方法。在完成调整之后,再次进行编译,会看到很快就能够得到我们所需要的 WASI 程序了。

代码语言:javascript
复制
du -hs *
4.0K funny.go
4.0K go.mod
4.0K go.sum
704K module.wasm

在 Node 中运行 WASI 标准的 WebAssembly 程序

在 Node.js 中运行 Wasm 有两种方案,一种是使用 Node 中的 WebAssembly 对象,直接运行传统的 Wasm 程序,另外一种则是使用 WASI 接口运行 Wasm 程序。

虽然第二种方案目前在 Node 中还处于实验状态,需要使用参数启用,但是毕竟是未来的标准,这里依旧推荐采用第二种方式。

代码语言:javascript
复制
const { readFileSync } = require('fs');
const { WASI } = require('wasi');
const { argv, env } = require('process');

(async function () {
    const wasi = new WASI({ args: argv, env });
    const importObject = { wasi_snapshot_preview1: wasi.wasiImport, /** or: wasi_unstable: wasi.wasiImport **/ };
    const wasm = await WebAssembly.compile(readFileSync("./module.wasm"));
    const instance = await WebAssembly.instantiate(wasm, importObject);
    wasi.start(instance);

    const { HelloWorld } = instance.exports;
    HelloWorld();
}());

将上面的内容保存为 index.js,接着使用 node --experimental-wasi-unstable-preview1 index.js 执行程序,可以看到我们成功的在 Node.js 中调用了使用 Go 编译的 Wasm 程序,输出了“艺术字”。

代码语言:javascript
复制
(node:15307) ExperimentalWarning: WASI is an experimental feature. This feature could change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
  _   _          _   _            __        __                 _       _
 | | | |   ___  | | | |   ___     \ \      / /   ___    _ __  | |   __| |
 | |_| |  / _ \ | | | |  / _ \     \ \ /\ / /   / _ \  | '__| | |  / _` |
 |  _  | |  __/ | | | | | (_) |     \ V  V /   | (_) | | |    | | | (_| |
 |_| |_|  \___| |_| |_|  \___/       \_/\_/     \___/  |_|    |_|  \__,_|

在浏览器中运行 WASI 标准的 WebAssembly 程序

前文中已经提到了一种方案,接下来我们来尝试第二种运行方案。首先准备项目目录,以及进行项目初始化。

代码语言:javascript
复制
mkdir /app/js-app
cd /app/js-app
npm init-y
npm i parcel parcel-bundler @wasmer/wasi @wasmer/wasmfs @wasmer/wasm-transformer --registry=https://registry.npmmirror.com
mkdir dist
cp module.wasm dist/

上面的命令执行完毕后,我们在项目目录的 package.json 中添加一个字段内容,尽可能的减少不必要的兼容性转换(你可以根据你的实际情况调整):

代码语言:javascript
复制
  "browserslist": [
    "last 1 Chrome versions"
  ],

创建一个用于展示的落地页面,index.html

代码语言:javascript
复制
<html>
  <body>
    <script src="./index.js" type="module"></script>
  </body>
</html>

然后,创建我们的核心脚本程序,index.js

代码语言:javascript
复制
import { WASI } from '@wasmer/wasi/lib'
import browserBindings from '@wasmer/wasi/lib/bindings/browser'
import { WasmFs } from '@wasmer/wasmfs'

const wasmFilePath = '/module.wasm'
const wasmFs = new WasmFs()

let wasi = new WASI({
  args: [wasmFilePath],
  env: {},
  bindings: {
    ...browserBindings,
    fs: wasmFs.fs
  }
})

const startWasiTask =
  async pathToWasmFile => {
    let response = await fetch(pathToWasmFile)
    let wasmBytes = new Uint8Array(await response.arrayBuffer())

    let wasmModule = await WebAssembly.compile(wasmBytes);
    let instance = await WebAssembly.instantiate(wasmModule, {
      ...wasi.getImports(wasmModule)
    });

    wasi.start(instance)
    instance.exports.HelloWorld()

    let stdout = await wasmFs.getStdOut()
    document.write(`<p>Standard Output:</p><pre>${stdout}</pre>`)
  }

startWasiTask(wasmFilePath)

文件都准备继续之后,使用 ./node_modules/.bin/parcel index.html --port=8081 启动服务,在浏览器中访问 localhost:8081,你将会看到调用 Wasm 程序输出的内容:

代码语言:javascript
复制
Standard Output:

  _   _          _   _            __        __                 _       _
 | | | |   ___  | | | |   ___     \ \      / /   ___    _ __  | |   __| |
 | |_| |  / _ \ | | | |  / _ \     \ \ /\ / /   / _ \  | '__| | |  / _` |
 |  _  | |  __/ | | | | | (_) |     \ V  V /   | (_) | | |    | | | (_| |
 |_| |_|  \___| |_| |_|  \___/       \_/\_/     \___/  |_|    |_|  \__,_|

浏览器中 Go Wasm 的程序输出

在 Go 程序中运行 WASI 标准的 WebAssembly

想在 Golang 中运行由 Golang 编写的具备 WASI 标准接口的 Wasm,其实还是有一点挑战的。一般情况下,你可能会遇到下面这些问题:

  • 首先,你暂时不能通过 Golang 的编译器构建一个标准的支持 WASI 标准的 Wasm 程序。
  • 其次,如果你使用比较流行的 wasmer-go 或者其他的运行时尝试执行标准的 WASI 程序,可能会遇到一些因为兼容性问题导致的报错,比如:``Missing import: `wasi_snapshot_preview1`.`fd_write```。
  • 最后,如果你侥幸修复了这些问题,你会发现在没有 WAT 文本格式文件的前提下,你还需要手动补全 Wasm 程序的导出函数,才能够正常使用程序。

关于上面的这些问题,在 wasmer-go 维护者的回答中曾提到,关于生成 WASI 标准的程序的方式,wasmer-go 项目的维护者们也不止一次的建议我们使用 TinyGo 替代默认的 Golang 编译器。如果你想使用 wasmer-go 来完成这件事,会遇到一些问题,维护者目前并不考虑朝着这个方向完善,并推荐我们使用 https://github.com/go-wasm-adapter/go-wasm 这个项目,来将上文中在浏览器中起到桥接作用的 JS Bridge 代码,在 Go 的代码中“运行一次”,将运行环境“垫平”。或考虑使用 https://github.com/mattn/gowasmer 的项目,针对 TinyGo 的产物进行“垫平”操作。

回想起文章一开始提到的,各种云服务网关都陆陆续续开始支持 WASM 的方式来扩展能力,而我们之前熟悉的 Traefik 却采用了类似 Nginx 的方案,则使用了另外一种更笨重的方案,官方团队提供基于 Golang 的 SDK,然后使用基于约定的方式动态从本地或远程加载这些同构的应用。采取这个技术路线的原因里,或许有一大部分正是出于上面的种种现实问题。

不过,2021 即将结束,这个问题还会是问题吗?

其实,早在今年年中的时候,wasmer-go 就可以通过 WASI 的方式来运行 Wasm 了,不过官方的项目缺少一个可以使用的示例。在经过一些尝试之后,我解决了这个问题,下面跟着我一起来玩吧。

先创建项目目录,进行一些初始化操作:

代码语言:javascript
复制
mkdir /app/go-app
cd /app/go-app/
go mod init soulteary.com/go-app/v2
cp /app/wasm/module.wasm .

接着,安装最新版本的 wasmer-go 项目运行时:

代码语言:javascript
复制
go get github.com/mattn/gowasmer

然后,编写一个简单的 Golang 程序,来加载 Wasm 程序,并执行它:

代码语言:javascript
复制
package main

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

将上面的内容保存为 main.go,然后执行 go run main.go。不出意外,你将会看到类似下面的结果:

代码语言:javascript
复制
  _   _          _   _            __        __                 _       _
 | | | |   ___  | | | |   ___     \ \      / /   ___    _ __  | |   __| |
 | |_| |  / _ \ | | | |  / _ \     \ \ /\ / /   / _ \  | '__| | |  / _` |
 |  _  | |  __/ | | | | | (_) |     \ V  V /   | (_) | | |    | | | (_| |
 |_| |_|  \___| |_| |_|  \___/       \_/\_/     \___/  |_|    |_|  \__,_|
<nil>

在Go中调用 Wasm 程序

在其他语言中运行 WASI 标准的程序

如果你对其他语言中运行 WASI 程序有需求,可以关注 https://github.com/wasmerio 这个项目。或者参考前文中提到的商业化公司或团队的实践,从他们的开源项目中剥离所需要的代码。

考虑到具体场景问题需要具体分析,这里就不做展开了,如果有必要,我会再写一篇文章,聊聊其他技术栈、应用生态中集成和使用 Wasm 程序。

最后

目前的 Wasm 领域的生态还有待完善,像极了十年前的前端生态。**Wasm 和前端技术一样,出现和发展不是为了取代谁,而是为了让解决事情的路径多一条,让已有多技术产品效能更高。**我始终相信会出现那么一批人,和十年前的老前端们一样,将它的生态完善起来的。

希望本文能够帮助到徘徊在这项技术前无从下手的你,并为你打开一扇新的大门。

君子以文会友,以友辅仁。

--EOF

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-11-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 折腾技术 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 写在前面
  • 适用场景 & 优势
  • 简单起步:浏览器中的 WebAssembly
    • 环境准备
      • 从 Golang 创建 WebAssembly 程序
        • 创建可与 JS 交互的 API 接口
          • TinyGo 异常报错修复
          • 进阶操作:拿 WebAssembly 当容器使用,构建通用 WASI 程序
            • 环境准备
              • 编写通用的 WASI 程序
                • 在 Node 中运行 WASI 标准的 WebAssembly 程序
                  • 在浏览器中运行 WASI 标准的 WebAssembly 程序
                    • 在 Go 程序中运行 WASI 标准的 WebAssembly
                      • 在其他语言中运行 WASI 标准的程序
                      • 最后
                      相关产品与服务
                      容器服务
                      腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
                      领券
                      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档