首页 > 其他分享 >Go每日一库之132:wasm与tinygo

Go每日一库之132:wasm与tinygo

时间:2023-09-29 21:16:08浏览次数:48  
标签:WebAssembly tinygo 一库 132 wasm Go go Wasm main

WASM 的概念,这几年还是挺火的,新的语言,比如 Rust、Go、Swift 等,都对 WASM 提供支持。相比之下,Go 语言的简单性,使得对 WASM 的支持,使用起来也较简单。本文是目前公开资料中为数不多较完整的教程,希望能对你有帮助。

WASM 是什么

标题说:“Golang 中的 Wasm 太棒了。”,但请用几句话来说“Wasm”是什么?
WebAssembly 主页说:“WebAssembly(缩写为 Wasm)是一种基于堆栈的虚拟机的二进制指令格式。Wasm 被设计为编程语言的可移植编译目标,支持在 Web 上部署客户端和服务器应用程序。”
总结就是:

  • “Wasm 是一种可移植的格式(如 Java 或 .Net),你可以在任何有支持它的主机的地方执行它。最初,主要的主机是带有浏览器的 JavaScript”

现在,你可以用 JavaScript 和 NodeJS 运行 Wasm,我们最近看到了像 Wasmer 项目这样的 Wasm 运行时的诞生,允许在任何地方运行 Wasm。
我喜欢说“一个 wasm 文件就像一个容器镜像,但更小,没有操作系统”

你可以用多种语言编译一个 Wasm 文件:C/C++、Rust、Golang、Swift ……我们甚至看到了专门用于构建 Wasm 的语言的出现,比如 AssemblyScript[1] 或有前途的 Grain[2](可以密切关注它,语法很可爱)。
今年夏天,我决定开始使用 Wasm。这种趋势似乎是使用 Rust,但我很快就明白我的小步骤会很复杂。困难不一定来自语言本身。最乏味和困难的部分是我在浏览器中运行一个简单的“Hello World”所需的所有工具。经过一番搜索,我发现 GolangWasm 提供了非常简单的支持(比 Rust 简单得多)。所以,我的假期作业是用 Golang 完成的。
Golang 对 Wasm 的支持非常棒。通常,WebAssembly 有四种数据类型(32 和 64 位整数,32 和 64 位浮点数),使用带有字符串参数(甚至 JSON 对象)的函数可能会很混乱。幸运的是,Go 提供了wasm_exec.js 与 JavaScript API 交互的文件。

WASM 在Go中的简单示例

创建项目:

 go-wasm/
 |__ out/
 |__ go/
     |__ main.go 

main.go:

package main

func main() {
   println("Hello World!!!")
}

运行 go 文件go run go/main.go。这将打印Hello World。

现在我们可以将它编译成 WebAssembly 模块并作为 WebAssembly 运行:

GOOS=js GOARCH=wasm go build -o out/main.wasm go/main.go

这将在out文件夹里生成main.wasm。与 Rust 不同,Golang 不会生成任何绑定文件。

拷贝wasm绑定文件wasm_exec.js到out目录中。

# go原生版本
cp "$(go env GOROOT)/misc/wasm/wasm_exec.js" ./out

在out目录中创建一个index.html文件并添加以下内容:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Go wasm</title>
  </head>

  <body>
    <script src="wasm_exec.js"></script>
    <script>
      const go = new Go()
      WebAssembly.instantiateStreaming(
        fetch('main.wasm'),
        go.importObject
      ).then((res) => {
        go.run(res.instance)
      })
    </script>
  </body>
</html>

也可以用这种更健壮的版本:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>Go wasm</title>
</head>

<body>
<script src="wasm_exec.js"></script>
<script>
    // This is a polyfill for FireFox and Safari
    if (!WebAssembly.instantiateStreaming) {
        WebAssembly.instantiateStreaming = async (resp, importObject) => {
            const source = await (await resp).arrayBuffer()
            return await WebAssembly.instantiate(source, importObject)
        }
    }

    // Promise to load the wasm file
    function loadWasm(path) {
        const go = new Go()

        return new Promise((resolve, reject) => {
            WebAssembly.instantiateStreaming(fetch(path), go.importObject)
                .then(result => {
                    go.run(result.instance)
                    resolve(result.instance)
                })
                .catch(error => {
                    reject(error)
                })
        })
    }

    // Load the wasm file
    loadWasm("main.wasm").then(wasm => {
        console.log("main.wasm is loaded ")
    }).catch(error => {
        console.error(error)
    })
</script>
</body>
</html>

最重要的部分是:
这行代码
和这一行WebAssembly.instantiateStreaming,它是允许加载 wasm 文件的 JavaScript API。

index.html加载wasm_exec.js文件。请注意,此文件适用于 Browser 和 NodeJS 环境,它导出一个Go对象。

然后我们添加一个本地脚本。在脚本中,我们执行以下操作:

  • Instantiate Go from the wasm_exec.js.
  • Fetch the WebAssembly Module using instantiateStreaming

接下来启动一个Web server尝试一下go wasm。

./webserver.go:

package main

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

const dir = "./out"

func main() {
        fs := http.FileServer(http.Dir(dir))
        log.Print("Serving " + dir + " on http://localhost:8080")
        http.ListenAndServe(":8080", http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
                resp.Header().Add("Cache-Control", "no-cache")
                if strings.HasSuffix(req.URL.Path, ".wasm") {
                        resp.Header().Set("content-type", "application/wasm")
                }
                fs.ServeHTTP(resp, req)
        }))
}

项目结构:

├── go
│   └── main.go
└── out
    ├── index.html
    ├── wasm_exec.js
    └── main.wasm
├── webserver.go    

运行:

go run webServer.go

打开http://localhost:8080 ,可以在console上显示Hello World!!!:
每日一库之132:wasm与tinygo-0

注意:
由于wasm是在网页上实时加载,所以生成的 WebAssembly 模块的文件大小非常重要。上例中仅仅一个 Hello World 就高达 1.3MB,对于现代网页来说是完全不能接受的。

-rw-r--r--  1 sendilkumar  staff   482B Jul  5 23:20 index.html
-rwxr-xr-x  1 sendilkumar  staff   1.3M Jul  5 23:19 main.wasm
-rw-r--r--  1 sendilkumar  staff    13K Jul  5 23:18 wasm_exec.js

或许WebAssembly能提供更优秀的性能,但如果引入的模块过大的话就不应当考虑它。

别当心,我们有Tiny GO!

TinyGO 使用

每日一库之132:wasm与tinygo-1

TinyGo 是一个将 Golang 带入微控制器和现代网络浏览器的项目。他们有一个基于 LLVM 编译的全新编译器。使用 TinyGo,我们可以生成经过优化以在芯片中执行的微小库。

TinyGo 允许为微控制器编译 Golang 源代码,它也可以将 Go 代码编译为 Wasm。TinyGo 是一个用于“小地方”的编译器,因此生成的文件要小得多。

查看如何安装tinygo:https://tinygo.org/getting-started/

安装后,您可以使用 TinyGo 编译任何 Golang 代码。我们可以使用以下命令将 Golang 编译成 WebAssembly 模块。

 
tinygo build -o out/main.wasm -target wasm ./go/main.go

使用tinygo编译后,wasm模块减小到3.8K:

-rw-r--r--  1 sendilkumar  staff   482B Jul  5 23:20 index.html
-rwxr-xr-x  1 sendilkumar  staff   3.8K Jul  5 23:29 main.wasm
-rw-r--r--  1 sendilkumar  staff    13K Jul  5 23:18 wasm_exec.js

使用tinygo编译的wasm模块,在加载的时候也要搭配tinygo版本的wasm_exec.js:

cp "$(tinygo env TINYGOROOT)/targets/wasm_exec.js" ./out/wasm_exec.js

# 或者从这里下载
https://github.com/tinygo-org/tinygo/blob/master/targets/wasm_exec.js

再重新启动webserver尝试下吧!

References

Go 中的 WASM 很棒:全网最全示例教程
tinygo快速入门
如何使用WebAssembly提升性能?
从首届 WebAssembly Summit 看 Wasm 未来发展方向
[

](https://blog.51cto.com/u_15057848/2567467)

标签:WebAssembly,tinygo,一库,132,wasm,Go,go,Wasm,main
From: https://www.cnblogs.com/arena/p/17737341.html

相关文章

  • Go每日一库之131:caddy(轻量web服务器)
    一直以来,我都是使用Nginx作为Web服务器,但是配置可以说是非常麻烦了。每次我要新开一个域名,都要先使用acme.sh签发SSL证书,然后再写配置,大概要花上5分钟的时间。曾经想过写个脚本自动完成这些工作,但是苦于对Linux的了解不多,也就作罢了。最近看到了Caddy,一个用Go写的......
  • Go每日一库之130:go-humanize(人性化显示)
    go-humanize是一个「人性化」的Go语言库,人性化的意思不是形容这个Go语言库,而是这个Go语言库实现的功能,它可以把数字、时间、容量等转换为我们人类容易理解的词语,比如硬盘的容量是82854982bytes,我们可不太好理解,但是如果说容量是83M,那就好理解了,go-humanize干的就是这个事情。......
  • Go每日一库之129:promu(Prometheus构建发布工具)
    众所周知,Go语言中打包命令是gobuild。在项目中,你可以单独使用gobuild命令对项目进行编译打包,也可以根据自己的需要,在该命令后加各种参数。prometheus官方为了统一项目(包括prometheus、alertmanager和各种官方的exporter)的编译和打包,开发了promu工具。官方对promu工具......
  • Go每日一库之128:podinfo(k8s微服务模板)
    项目介绍官方Github:PodinfoPodinfo是一个用Go制作的小型web应用程序,它展示了在Kubernetes中运行微服务的最佳实践。它已实现的技术指标(截选自官方README.md):里面每一项技术指标的实现方式,其实都可以拿出来单独讲好久,相关理论也有好多。这里我只是讲针对这个项......
  • Go每日一库之145:MinIO(高性能对象存储)
    1.MinIO简介MinIO是一个基于Go实现的高性能、兼容S3协议的对象存储。它采用GNUAGPLv3开源协议,项目地址是https://github.com/minio/minio,官网是https://min.io。它适合存储海量的非结构化的数据,例如说图片、音频、视频等常见文件,备份数据、容器、虚拟机镜像等等,小......
  • Go每日一库之146:bbs-go(bbs框架)
    概要bbs-go是一款基于Go语言研发的开源、前后端分离、精美小巧、跨平台的社区系统。初期该项目仅用过学习和交流,开源之后越来越多的小伙伴儿开始喜欢和关注他,这也是我长期升级和维护的动力。bbs-go为前后端分离设计,后端接口服务使用简洁的Go语言进行开发,前端页面使用Vue.js进......
  • Go每日一库之144:go-obs-websocket(OBS连接器)
    推荐理由互联网的兴起带动了直播行业的火热,除了少数直播网站有自己的推流工具之外,OBS是主流的推流工具,广泛应用在直转播技术之上。简介go-obs-websocket是一个与OBS进行websocket通信的连接库,具备调用大部分OBS功能的接口,在互动直播和智能转播技术上广泛应用。快速开始安装g......
  • Go每日一库之143:servicegroup(进程内优雅管理多个服务)
    前言在go-zero社区里,经常会有同学问,把APIgateway和RPCservice放在同一个进程内可不可以?怎么弄?有时也会有同学把对外服务和消费队列放在一个进程内。咱们姑且不说此种用法合理与否,因为各个公司的业务场景和开发模式的差异,我们就只来看看此类问题怎么解比较优雅。问题举例......
  • Go每日一库之141:go-bindata(嵌入静态文件)
    使用Go开发应用的时候,有时会遇到需要读取静态资源的情况。比如开发Web应用,程序需要加载模板文件生成输出的HTML。在程序部署的时候,除了发布应用可执行文件外,还需要发布依赖的静态资源文件。这给发布过程添加了一些麻烦。既然发布单独一个可执行文件是非常简单的操作,就有人会......
  • Go每日一库之140:Zinc(轻量级搜索引擎)
    ‍项目介绍Zinc是一个轻量级替代Elasticsearch的开源搜索引擎。Elasticsearch真的好用,但是Elasticsearch安装和配置也是真的繁琐,后续的一些维护也有一定成本。另外一个Elasticsearch的不足就是服务运行起来需要的计算资源较多,对于普通的用户来说是有点浪费的。Zinc,拥有......