Cloudflare Workers 是一款 Severless 佈署平台,預設使用 JavaScript 作為開發語言(更新:最近也支援了原生 Rust)。在官方的文件中,雖然有各種語言轉換成 JavaScript 或編成 Wasm 的例子,但卻未有針對 Go 作出進一步說明。本文章將會介紹如何把 Go 編譯出來的 Wasm 佈署到 Cloudflare Workers 中。
編譯 Wasm
由於 Go 原生支援 Wasm,因此編譯起來十分簡單。只需執行以下指令:
GOOS=js GOARCH=wasm go build -o main.wasm main.go
另外,也可以使用 TinyGo 來編譯,使 Wasm 檔案更小。
佈署前準備
一般來說 Go 在執行 Wasm 時,需要在 JavaScript 的 context 中先運行 wasm_exec.js,但如果直接使用這個官方的檔案,無法在 Worker 的環境下直接執行,原因是因為 Worker 不支援:
因此需要稍作修改才能正常使用,另外在編寫 Go 程式時也要注意避免使用會用到以上功能的 Function。
- 針對
fs
部份,需要把require
的程式碼刪去(第 31 至 36 行) - 針對
performance.now
的部份,需要自己另外加入 polyfill:
globalThis.performance = {
now: Date.now,
};
目前在 Go 的社群中,也有討論到因為需要支援不同環境的 Wasm,因此目前正在修改
wasm_exec.js
,讓用戶自行定義所需的 polyfill。相關討論
佈署到 Cloudflare Workers
方法 1:使用 REST API 上傳 metadata
優點
- 不需安裝附加的軟件便能上傳
缺點
- 難以在本地端除錯或測試
- 需要自行打包 JavaScript
1. 先準備好 Worker 程式碼
import "./wasm_exec_worker_polyfill.js";
import "./wasm_exec.js";
async function handleRequest(request) {
// init go wasm instance
const go = new globalThis.Go();
const instance = await WebAssembly.instantiate(WASM, go.importObject);
go.run(instance);
// call wasm functions here
const r = globalThis.someWasmFunction(body);
return new Response(r, {});
}
addEventListener("fetch", (event) => {
event.respondWith(handleRequest(event.request));
});
2. 打包 JavaScript
這邊介紹的是使用 Parcel 2 進行打包,另外也可以視乎個人習慣使用 Webpack, gulp 等工具。
在 package.json
的 script
加入 parcel build --no-source-maps ./main.js
執行後在 ./dist
將會出現一個打包好的 main.js
。
3. 準備 metadata
以下 metadata.json
設定將會將你的 .wasm
在 JavaScript 中 bind 成 WASM
這個變數。
{
"body_part": "script",
"bindings": [
{
"type": "wasm_module",
"name": "WASM",
"part": "wasm"
}
]
}
3. 上傳
確保已準備好 metadata.json
, ./dist/main.js
和 main.wasm
;
及設定環境變數 SCRIPT_NAME
, CF_ACCOUNT_ID
, CF_API_TOKEN
。
curl -X PUT "https://api.cloudflare.com/client/v4/accounts/$CF_ACCOUNT_ID/workers/scripts/$SCRIPT_NAME" \
-H "Authorization: Bearer $CF_API_TOKEN" \
-F "[email protected];type=application/json" \
-F "script=@dist/main.js;type=application/javascript" \
-F "[email protected];type=application/wasm"
完成後便能在 Cloudflare Dashboard 的 Workers 版面看到剛上傳的 script。
參考:Let’s build a Cloudflare Worker with WebAssembly and Haskell
方法 2:使用 Wrangler
目前在 Cloudflare Worker 官方文件未有 Go 使用 Wrangler 佈署的例子。
優點
- 方便在本地端除錯及測試
- 毋需自行打包 JavaScript
缺點
- Wrangler 需額外安裝
- 使用 ES Module 格式佈署後,在 Dashboard 暫未支援 Quick edit
安裝 Wrangler
npm i @cloudflare/wrangler -g
1. 準備 Worker 程式碼 (ES Module)
在 Wrangler 要上傳 Wasm 需要 modules
format 才能支援 CompiledWasm
,因此這邊用了 ES Modules 語法,與方法 1 的語法稍有分別。
import "./wasm_exec_worker_polyfill.js";
import "./wasm_exec.js";
import WASM from "./main.wasm";
export default {
async fetch(request, env, ctx) {
// init go wasm instance
const go = new globalThis.Go();
const instance = await WebAssembly.instantiate(WASM, go.importObject);
go.run(instance);
// call wasm functions here
const r = globalThis.someWasmFunction(body);
return new Response(r, {});
},
};
2. 設定 Wrangler
參考:官方文件
name = ""
type = "javascript"
account_id = ""
zone_id = ""
workers_dev = true
[build.upload]
format = "modules"
main = "./main.mjs"
[[build.upload.rules]]
globs = ["**/*.wasm"]
type = "CompiledWasm"
2. 除錯及佈署
Wrangler 本身已經提供了本地除錯及佈署的功能,因此在設定好 wrangler.toml
後,便可以立即執行。
除錯:
wrangler dev
佈署:wrangler publish