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