Open5

GolangでCloudflare Workersのツールを作成する

kazuphkazuph

https://github.com/syumai/workers

setup

npm install -g wrangler
go install golang.org/x/tools/cmd/gonew@latest

init

cd
cd src/github.com/kazuph
gonew github.com/syumai/workers/_templates/cloudflare/worker-go github.com/kazuph/go-graph-worker
cd go-graph-worker
go mod tidy
make dev # start running dev server
curl http://localhost:8787/hello # outputs "Hello!"
kazuphkazuph

https://github.com/gonum/plot

を使って、配列を投げるとグラフ描画してくれるWorkerを作成する。

main.go
package main

import (
	"encoding/json"
	"net/http"

	"github.com/syumai/workers"

	"gonum.org/v1/plot"
	"gonum.org/v1/plot/plotter"
	"gonum.org/v1/plot/vg"
)

func main() {
	// /plotエンドポイント: JSON配列を受け取り、グラフをPNGで返す
	http.HandleFunc("/plot", func(w http.ResponseWriter, req *http.Request) {
		// JSONデコード
		var data []float64
		if err := json.NewDecoder(req.Body).Decode(&data); err != nil {
			http.Error(w, "invalid json", http.StatusBadRequest)
			return
		}

		// グラフ生成
		p := plot.New()
		p.Title.Text = "Example Plot"
		p.X.Label.Text = "X"
		p.Y.Label.Text = "Y"

		// dataをプロット用の点列に変換
		pts := make(plotter.XYs, len(data))
		for i, v := range data {
			pts[i].X = float64(i)
			pts[i].Y = v
		}

		line, err := plotter.NewLine(pts)
		if err != nil {
			http.Error(w, "failed to create line plotter", http.StatusInternalServerError)
			return
		}
		p.Add(line)

		// レスポンスをPNGで返す
		w.Header().Set("Content-Type", "image/png")

		// p.Saveではファイルに書き出すため、代わりに直接ResponseWriterに出力するには
		// p.WriterToを使う
		writerTo, err := p.WriterTo(4*vg.Inch, 4*vg.Inch, "png")
		if err != nil {
			http.Error(w, "failed to create writer", http.StatusInternalServerError)
			return
		}
		_, err = writerTo.WriteTo(w)
		if err != nil {
			http.Error(w, "failed to write image", http.StatusInternalServerError)
			return
		}
	})

	workers.Serve(nil) // use http.DefaultServeMux
}

dev

make dev

test

curl -X POST \
  -H "Content-Type: application/json" \
  -d '[1,2,3,4,3,2,1]' \
  http://localhost:8787/plot \
  -o output.png

kazuphkazuph

deploy

❯ make deploy
wrangler deploy

 ⛅️ wrangler 3.96.0
-------------------

Running custom build: make build
make[1]: Entering directory '/home/kazuph/src/github.com/kazuph/go-graph-worker'
go run github.com/syumai/workers/cmd/workers-assets-gen@v0.23.1 -mode=go
GOOS=js GOARCH=wasm go build -o ./build/app.wasm .
du -sh ./build
13M     ./build
make[1]: Leaving directory '/home/kazuph/src/github.com/kazuph/go-graph-worker'
Total Upload: 12878.22 KiB / gzip: 4769.75 KiB
Uploaded go-worker (5.13 sec)
Deployed go-worker triggers (0.72 sec)
...

圧縮後4MB強だったので無料プランだとだめでしたが、有料プランなのでデプロイして動作確認までできました。

kazuphkazuph

tinygo

install

wget https://github.com/tinygo-org/tinygo/releases/download/v0.34.0/tinygo_0.34.0_amd64.deb
sudo dpkg -i tinygo_0.34.0_amd64.deb

Makefileに追加

.PHONY: tinybuild
tinybuild:
	go run github.com/syumai/workers/cmd/workers-assets-gen@v0.23.1
	tinygo build -o ./build/app.wasm -target wasm -no-debug ./...

サイズ比較

# go
❯ du -sh ./build                                                  
13M     ./build

# tinygo
❯ du -sh ./build
6.7M    ./build

error

tinygo build -o ./build/app.wasm -target wasm -no-debug ./...
⎔ Starting local server...
panic: reflect: unimplemented: AssignableTo with interface
✘ [ERROR] A hanging Promise was canceled. This happens when the worker runtime is waiting for a Promise from JavaScript to resolve, but has detected that the Promise cannot possibly ever resolve because all code and events related to the Promise's I/O context have already finished.


[wrangler:inf] POST /plot 500 Internal Server Error (57ms)
✘ [ERROR] Uncaught (in response) Error: The script will never generate a response.

一旦時間切れ。

kazuphkazuph

tinygoリベンジ

cd
cd src/github.com/kazuph
gonew github.com/syumai/workers/_templates/cloudflare/worker-tinygo github.com/kazuph/tinygo-graph-worker
cd tinygo-graph-worker
go mod tidy

....

一応テンプレートの違いの可能性も感じてやったのですが、変わらず。