惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

T
Troy Hunt's Blog
F
Fortinet All Blogs
D
DataBreaches.Net
Google DeepMind News
Google DeepMind News
Y
Y Combinator Blog
The Register - Security
The Register - Security
T
Tailwind CSS Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
月光博客
月光博客
V
Vulnerabilities – Threatpost
S
Securelist
S
SegmentFault 最新的问题
T
Threat Research - Cisco Blogs
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
P
Privacy International News Feed
S
Schneier on Security
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
L
LangChain Blog
GbyAI
GbyAI
Apple Machine Learning Research
Apple Machine Learning Research
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
美团技术团队
Cyberwarzone
Cyberwarzone
C
Cisco Blogs
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Google Online Security Blog
Google Online Security Blog
M
MIT News - Artificial intelligence
U
Unit 42
V
V2EX
C
CERT Recently Published Vulnerability Notes
云风的 BLOG
云风的 BLOG
B
Blog
博客园 - 叶小钗
Attack and Defense Labs
Attack and Defense Labs
Security Archives - TechRepublic
Security Archives - TechRepublic
aimingoo的专栏
aimingoo的专栏
Hacker News: Ask HN
Hacker News: Ask HN
博客园 - Franky
Engineering at Meta
Engineering at Meta
Schneier on Security
Schneier on Security
C
CXSECURITY Database RSS Feed - CXSecurity.com
酷 壳 – CoolShell
酷 壳 – CoolShell
T
The Blog of Author Tim Ferriss
IT之家
IT之家
W
WeLiveSecurity
Cisco Talos Blog
Cisco Talos Blog
K
Kaspersky official blog
Martin Fowler
Martin Fowler
SecWiki News
SecWiki News

陈少文的网站

巨变与机遇的未来十年 Kubernetes 平台管理软件压力测试方案 使用镜像部署 Hexo 静态页面 终于等到你 - GitHub 镜像仓库服务(ghcr.io) 一起来学 Go --(6)Interface 一起来学 Go --(5)Goroutine 和 Channel 什么是函数式编程 如何在 Kubernetes 集群集成 Kata 柯里化与偏函数 使用 PyGithub 自动创建 Label 软件产品是团队能力的输出 Helm 2 、Helm 3 比较 IoT 变现 Kubernetes 中的 DNS 服务 国内的 Helm 镜像源 Harbor 使用自签证书支持 Https 访问 DevOps 工具链之 Prow 如何使用 kfctl 安装 Kubeflow VS Code 无法下载 Go 插件的工具包 工程师更应具有服务精神 你不知道的 Docker 使用技巧 使用 Docker 运行 Tensorflow 论中国 什么是左移 如何清空 Git 仓库全部历史记录 一禅小和尚 有风吹过厨房 时间的玫瑰 如何在 CentOS 安装 GPU 驱动 开发 Tips(19) 使用 Velero 备份 Kubernetes 集群 Kubernetes Cheat Sheet 开发 Tips(18) 如何构建一个 Java 工程 开发 Tips(17) KubeSpray 安装 Kubernetes 报错 ip in ansible_all_ipv4_addresses 基于 Kubernetes 和 Jenkins 搭建自动化测试系统 在 Kubernetes 上动态创建 Jenkins Slave 使用 Jenkins 进行服务拨测 开发 Tips(16) Kubernetes 签发 Ingress 证书及日常故障运维 Kubernetes 中 Deployment 的基本操作 Kubernetes 中的证书 如何使用 KubeBuilder 开发一个 Operator Kubernetes 1.6.0 安装问题汇总 镜像管理工具 -- Harbor 开发 Tips(15) Docker 如何拉取镜像 开发 Tips(14) 使用 Helm 安装 harbor 开发 Tips(13) 使用 S2I 构建云原生应用 在 Kubernetes 中使用 emptyDir、hostPath、localVolume 开发 Tips(12) 开发 Tips(11) 代码质量分析工具 SonarQube 使用 Kubeadm 安装 Kubernetes 集群 一起来学 Go --(4)常用函数 Kubernetes 中的 Ceph Kubernetes 之 Volumes Kubernetes 之 Labels、Selectors 开发 Tips(10) 开源正在重构商业模式 Kubernetes 之网络 Kubernetes 之 API 使用 Helm 和 Operator 快速部署 Prometheus Kubernetes 复杂有状态应用管理框架 -- Operator Kubernetes 的包管理器 -- Helm 一起来学 Go --(3)Go Modules 如何一步一步地优化博客方案 kubectl 实用指南 Kubernetes 中的基本概念 搭建远程 Kubernetes 开发环境 大公司和小公司的 ToB 思路 开发 Tips(9) Go 入门指南 一起来学 Go --(2)数据与逻辑结构 如何预防 Web 富文本中的 XSS 攻击 django-xss-cleaner 云工作时代 一起来学 Go --(1)背景与特点 SaaS 开发团队的不同阶段 你不知道的 Git 使用技巧 输出既服务 微服务设计 继续奔跑 开发 Tips(8) 从账户安全到二次验证 Django 性能之数据库查询优化 Django 性能之分库分表 敏捷开发之研发流程 打造一致性的团队 开发 Tips(7) Pytest 进阶学习之 Mock PaaS 部署之 buildpack Go 开发配置 领域输出才是 PaaS 的核心竞争力 Pytest 入门学习 开发 Tips(6) 如何使用 Jenkins、Docker、GitLab 搭建 Django 自动化部署流程
使用 Go 编写 WebAssembly 程序
微信公众号 · 2023-05-13 · via 陈少文的网站

1. WebAssembly 简介

  • 跨平台性,可以在任何支持 WebAssembly 的平台上运行,包括 Web 浏览器、服务器、移动设备等

  • 高性能,采用了一种紧凑的二进制格式,可以在浏览器中快速加载和解析,从而提高应用程序的性能

  • 安全性,采用了一种沙箱模型,可以隔离运行在其中的代码,从而保护系统免受恶意代码的攻击

  • 可移植性,WebAssembly 的代码可以通过编译器从不同的编程语言中生成,因此可以轻松地将现有的代码转换为 WebAssembly 格式

  • 可扩展性,WebAssembly 支持向后兼容的版本控制,并允许在未来添加新的指令和功能,从而使其具有更好的扩展性

2. 落地案例

更多使用 WebAssembly 的案例参考 https://madewithwebassembly.com/

3. 常见 WebAssembly 非前端运行时

WebAssembly 运行时是 WebAssembly 代码的运行环境,它负责加载 WebAssembly 模块、创建 WebAssembly 实例、执行 WebAssembly 代码。

  • C/C++ 语言实现的 - WasmEdge、wasm3
  • Rust 语言实现的 - wasmer、wasmtime
  • Go 语言实现的 - WaZero

目前的主流浏览器、高版本的 Nodejs 都是支持 wasm 的。

4. 安装 WasmEdge

由于 Docker 新版本支持 WasmEdge,我在本地也选择使用 WasmEdge 作为 WebAssembly 运行时,以便于容器内保持一致。

  • 下载 Wasmedge 二进制文件

前往 https://github.com/WasmEdge/WasmEdge/releases 下载对应平台的二进制文件。

1
wget https://github.com/WasmEdge/WasmEdge/releases/download/0.12.0/WasmEdge-0.12.0-darwin_x86_64.tar.gz
  • 解压 Wasmedge 并安装
1
2
tar -zxvf WasmEdge-0.12.0-darwin_x86_64.tar.gz -C /Users/shaowenchen --strip-components=1
export PATH=$PATH:/Users/shaowenchen/bin

如果需要拷贝到其他目录下,注意保持 bin、include、lib 三个目录的相对位置,否则会出现以下错误:

1
2
3
dyld: Library not loaded: @rpath/libwasmedge.0.dylib
  Referenced from: /Users/shaowenchen/bin/wasmedge
  Reason: image not found
  • 查看 Wasmedge 版本
1
2
3
wasmedge --version

wasmedge version 0.12.0

5. 使用 TinyGo 编译 WebAssembly 程序

5.1 使用 TinyGo 的优劣

  • 使用 TinyGo 的好处

TinyGo 是 Go 的子集,可以使用 Go 语言编写 WebAssembly 程序。

可以直接在 WasmEdge 上运行,无需 JavaScript 环境。

wasm 文件足够小,几十 KB。

  • 使用 TinyGo 的缺点

TinyGo 有些库不能用,比如 net/http 等,具体可参考 https://tinygo.org/docs/reference/lang-support/stdlib/

TinyGo 不支持全部 Go 语言特性,比如 Cgo 等,具体可参考 https://tinygo.org/docs/reference/lang-support/

5.1 Hello, World!

  • 新建 main.go 文件
1
2
3
4
5
package main

func main() {
	println("Hello, World! by TinyGo")
}
  • 编译代码
1
tinygo build -o ./build/main.wasm -target=wasm
  • 使用 WasmEdge 运行
1
2
3
wasmedge ./build/main.wasm

Hello, World! by TinyGo
  • 新建 Dockerfile 文件
1
2
3
FROM scratch
ADD ./build/main.wasm /build/main.wasm
ENTRYPOINT ["/build/main.wasm"]
  • 编译容器镜像
1
docker build -t shaowenchen/wasm-hello-world:tinygo .
  • 运行容器镜像
1
2
3
4
docker run  --rm --runtime=io.containerd.wasmedge.v1 \
				shaowenchen/wasm-hello-world:tinygo

Hello, World! by TinyGo

6. 使用 Go 编译 WebAssembly 程序

6.1 使用 Go 的优劣

  • 使用 Go 的好处

可以使用 syscall/js 包与 JavaScript 进行交互。

可以使用 Go 语言的全部特性和包。

未来,WebAssembly 运行时支持 GC 后,程序体积、性能可能会得到改善。

  • 使用 Go 的缺点

默认需要 JavaScript 环境支持。

体积较大,几 MB、甚至几十 MB。不过,很多祖传项目编译出来也是几十、上百 MB。

6.2 Hello, World!

  • 新建 main.go 文件
1
2
3
4
5
package main

func main() {
	println("Hello, World! by Go 1.19")
}
  • 编译代码
1
GOOS=js GOARCH=wasm go build -o ./dist/main.wasm
  • 使用 node 运行

注意,nodejs 需要 12 及以上才行。

1
2
3
4
cp $(shell go env GOROOT)/misc/wasm/wasm_exec_node.js ./dist/
node ./dist/wasm_exec_node.js ./dist/main.wasm

Hello, World! by Go 1.19
  • 新建 Dockerfile 文件
1
2
3
FROM node:12-alpine
ADD ./dist /dist
ENTRYPOINT ["node","/dist/wasm_exec_node.js", "/dist/main.wasm"]
  • 编译容器镜像
1
docker build -t shaowenchen/wasm-hello-world:go .
  • 运行容器镜像
1
2
3
docker run  --rm shaowenchen/wasm-hello-world:go

Hello, World! by Go 1.19

6.3 使用 syscall/js 实现 Go 与 JavaScript 数据的交互

Go 语言提供了 syscall/js 包,可以用于与 JavaScript 进行交互。

  • 在 Go 中调用 JavaScript 函数

通过 syscall/js 包提供的 Global() 函数获取宿主 JavaScript 环境的对象。

1
js.Global().Get("console").Get("log").Invoke("Hello, World!")

等价于

1
console.log("Hello, World!");
  • 在 JavaScript 中调用 Go 函数
1
2
3
4
5
js.Global().Set("myfunc", js.FuncOf(myfunc))

func myfunc(this js.Value, args []js.Value) interface{} {
	return nil
}

js.Global().Set 将对象注册到 JavaScript 环境中,在浏览器前端可以直接调用 myfunc 函数。

  • 重写 hello world,在浏览器页面执行 wasm
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package main

import (
	"syscall/js"
)

func main() {
	// 调用前端的函数,在控制台打印
	js.Global().Get("console").Get("log").Invoke("Hello, World by Go")
	// 找到 ID 为 hello 的元素,插入文本
	js.Global().Get("document").Call("getElementById", "hello").Set("innerHTML", "Hello, World! by Go")
  // 将 Go 函数,注册到前端
	js.Global().Set("myfunc", js.FuncOf(myfunc))
  // 不立马退出,否则会报错
	<-make(chan bool)
}

func myfunc(this js.Value, args []js.Value) interface{} {
	println("myfunc called")
	// 获取函数参数
	myfunc_arg0 := args[0].String()
	// 在前端 window 对象中设置变量
	js.Global().Set("myfunc_arg0", myfunc_arg0)
	js.Global().Get("console").Get("log").Invoke(myfunc_arg0)
	return nil
}
  • 拷贝 wasm_exec.js wasm_exec.html 文件

Go 编译器自带有一个样例。

1
2
cp $(shell go env GOROOT)/misc/wasm/wasm_exec.js ./dist/
cp $(shell go env GOROOT)/misc/wasm/wasm_exec.html ./dist/

wasm_exec.js 的作用是在浏览器中加载执行 wasm,wasm_exec.html 是一个实例。

  • 修改 wasm_exec.html

为了演示 Go 与 JavaScript 的数据交互能力,这里稍微修改一下样例。

1
WebAssembly.instantiateStreaming(fetch("main.wasm")...

这里的 fetch 应该是编译出来的 wasm 文件,默认是 test.wasm,根据需要修改为 main.wasm

1
2
3
4
5
6
7
</script>
function callGo() {
			myfunc("abc");
		}
</script>
<button onClick="callGo();" id="callGoButton" >callGoButton</button>
<div id="hello"></div>

这里新增一个按钮,用于调用 Go 函数 myfunc。

  • 查看效果

本地启一个 http 服务

1
2
3
python3 -m http.server --directory ./dist

Serving HTTP on :: port 8000 (http://[::]:8000/) ...

访问 http://localhost:8000/wasm_exec.html 页面

点击 Run 按钮,Go 调用前端对象,输出文本、在页面插入文本。

点击 callGoButton,前端调用 Go 函数。

在控制台访问 windows.myfunc_arg0,获取 Go 在前端对象中设置的值。

7. 总结

本篇主要是在尝试 Go 编写 WebAssembly 的一些方式。如果不进行额外的配置,目前比较快捷的两种方式是:

  • 使用 TingyGo 编写,直接在 WasmEdge 上运行
  • 使用 Go 编写,需要借助 JS 加载,在 Nodejs 上运行

另外,还提供了 Go 1.19 编写 WebAssembly 与 JavaScript 函数、数据交互的一个示例。

文中相关代码都在 https://github.com/shaowenchen/demo/tree/master/wasm-hello-world

8. 参考