Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Go言語で始めるCloudflare Workers
Search
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
syumai
December 15, 2023
Programming
4.4k
9
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Go言語で始めるCloudflare Workers
https://github.com/syumai/workers
の紹介です。
syumai
December 15, 2023
More Decks by syumai
See All by syumai
作って学ぶ、 JSX (TSX) ランタイムの基本
syumai
7
1.7k
Oxlintのカスタムルールの現況
syumai
6
1.2k
Oxlintはいかにしてtsgolintのlint ruleを呼び出しているのか
syumai
2
1.3k
『[入門] Cloudflare Workers』本はなぜ誕生したのか
syumai
0
420
tsgolintはいかにしてtypescript-goの非公開APIを呼び出しているのか
syumai
9
3.1k
知られているようで知られていない JavaScriptの仕様 4選
syumai
3
1.3k
CloudflareのSandbox SDKを試してみた
syumai
0
900
実践AIチャットボットUI実装入門
syumai
9
4.3k
ProxyによるWindow間RPC機構の構築
syumai
3
1.5k
Other Decks in Programming
See All in Programming
エンジニア向け会社紹介/Findy Company Profile
findyinc
6
360k
Go1.27で導入されるジェネリクスメソッドでできること
mackee
0
250
ADKを使って簡単にAIエージェントを作ってみよう
k1mu21
0
290
エンジニアと一緒にテストコードの設計と実装を改善した話
mototakatsu
0
250
はてなアカウント基盤 State of the Union
cockscomb
1
1.3k
AI駆動開発を妨げる技術的負債の解消アプローチ / ai-refactoring-approach
minodriven
17
8.7k
Language Server 使ってる? 〜VSCode と Zed の場合〜 / Are you using a Language Server? ~For VS Code and Zed~
handlename
0
830
決定論的オーケストレーションの設計と実装 / Design and Implementation of Deterministic Orchestration
nrslib
4
1.6k
TypeScript+Orvalで実現する型安全かつ堅牢でスケーラブルなマルチチャネル通知基盤 / TSKaigi Night talks ~after conference~
d0riven
0
390
PHPだって関数型したい 〜できること、できないこと〜 / fp-in-php
jsoizo
0
160
ローカルLLMでどこまでコードが書けるか -拡張版 / How much code can be written on a local LLM Extended
kishida
12
4.6k
鹿野さんに聞く!『TypeScriptコードレシピ集』で磨く実践力
tonkotsuboy_com
4
1k
Featured
See All Featured
[Rails World 2023 - Day 1 Closing Keynote] - The Magic of Rails
eileencodes
38
2.9k
Why Your Marketing Sucks and What You Can Do About It - Sophie Logan
marketingsoph
0
190
Color Theory Basics | Prateek | Gurzu
gurzu
0
380
Visualizing Your Data: Incorporating Mongo into Loggly Infrastructure
mongodb
49
10k
Bash Introduction
62gerente
615
220k
jQuery: Nuts, Bolts and Bling
dougneiner
66
8.5k
Un-Boring Meetings
codingconduct
0
330
Building AI with AI
inesmontani
PRO
1
1.1k
How to Build an AI Search Optimization Roadmap - Criteria and Steps to Take #SEOIRL
aleyda
1
2.1k
The State of eCommerce SEO: How to Win in Today's Products SERPs - #SEOweek
aleyda
2
11k
Chasing Engaging Ingredients in Design
codingconduct
0
230
The Web Performance Landscape in 2024 [PerfNow 2024]
tammyeverts
12
1.2k
Transcript
Go で始めるCloudflare Workers syumai Workers Tech Talks #2 (2023/12/15)
自己紹介 syumai ECMAScript 仕様輪読会 主催 株式会社ベースマキナで管理画面のSaaS を開発中 Go でGraphQL サーバー
(gqlgen) や TypeScript でフロント エンドを書いています Software Design 12 月号からCloudflare Workers の連載をして ます Twitter: @__syumai Website: https://syum.ai
None
ベースマキナとは? DB やAPI の接続設定 & 呼び出し設定をするだけで、簡単にUI 生成が行える管理画面 SaaS API 呼び出しへの権限設定や、レビュー依頼
/ 承認機能も簡単に使えます https://about.basemachina.com
本日話すこと syumai/workers の紹介 デモ syumai/workers の仕組み どんなアプリケーションが動くか パフォーマンス 注意点 今後の展望
syumai/workers の紹介
workers https://github.com/syumai/workers http.Handler を作って、 workers.Serve に渡すだけでCloudflare Workers 上でHTTP サ ーバーとして動作する
http.ListenAndServe の代わりに workers.Serve 関数を呼ぶ形にするだけ TinyGo / Go を使ってWebAssembly を生成して実行する JavaScript 側のコードを触る必要が無い Cloudflare の機能のバインディングを提供 (R2, KV, D1 など)
作ったモチベーション WebAssembly とJavaScript の知識が無くてもGo でWorker を書けるようにしたかった 最終的に、gonew コマンドでtemplate からプロジェクト生成するだけで済む形に なった
実用的なWorker をGo で短時間で実装できるようなライブラリが欲しかった
デモ
デモ syumai/workers のQuick Start を実行してみます
Quick Start 必要なツール Node.js ( とnpm) wrangler npm install -g
wrangler . tinygo 0.29.0 以上 gonew go install golang.org/x/tools/cmd/gonew@latest 最近Go 公式から試験的なものとして出た、Go のプロジェクトをテンプレートから 立ち上げるためのコマンド
プロジェクトの作成 $ gonew github.com/syumai/workers/_templates/cloudflare/worker-tinygo your.module/my-app $ cd my-app $ go
mod tidy $ make dev # 開発サーバーの起動 $ curl http://localhost:8787/hello # outputs "Hello!" 今回は、 モジュール名を github.com/syumai/workers-tech-talk/hello-tinygo に します
ファイル構成 ├── Makefile ├── README.md ├── build # ここはいじる必要がない │
├── app.wasm │ ├── polyfill_performance.js │ ├── shim.mjs │ ├── wasm_exec.js │ └── worker.mjs ├── go.mod ├── go.sum ├── main.go # アプリケーション本体 └── wrangler.toml
アプリケーションの中身 func main() { http.HandleFunc("/hello", func(w http.ResponseWriter, req *http.Request) {
msg := "Hello!" w.Write([]byte(msg)) }) http.HandleFunc("/echo", func(w http.ResponseWriter, req *http.Request) { io.Copy(w, req.Body) }) workers.Serve(nil) // use http.DefaultServeMux } 普通のHTTP Server 同様、HandleFunc を使っているだけ /hello ハンドラーの実装を変更すると、curl の結果が変わるのが確認できる
デプロイ make deploy で完了 アプリ名を変えたい場合は、 wrangler.toml を編集する
syumai/workers の仕組み
syumai/workers の仕組み Cloudflare Workers は、基本的にRequest オブジェクトを処理し、Response オブジェクトを 返すと言う構造になっているため、下記が実装できればOK JavaScript 側で受け取ったRequest
オブジェクトをGo に渡す Go 側でResponse オブジェクトを組み立ててJavaScript 側に渡す これをGo の標準ライブラリのsyscall/js を使って実装した
syumai/workers の仕組み Worker のendpoint は(当然ながら)JS JS 側は、Go 側の初期化処理が終わるのを待つためのPromise を作成してglobalThis に設
定する Go 側の初期化処理で、Go にJS のRequest オブジェクトを渡すための handleRequest 関数をglobalThis に設定する Go 側の初期化処理が終わったら、Promise を解決 JS 側からhandleRequest 関数を呼んで、結果を返す
Worker のendpoint (JS 側) のコード async function run() { //
Go側の初期化完了を待つPromiseをglobalThisに設定 const readyPromise = new Promise((resolve) => { globalThis.ready = resolve; }); const instance = new WebAssembly.Instance(mod, go.importObject); go.run(instance); await readyPromise; } // Fetch Handlerを使うのは普通のWorkerと同じ export async function fetch(req, env, ctx) { await run(); // RequestをJS側からGo側に渡して処理する return handleRequest(req, { env, ctx }); } https://github.com/syumai/workers/blob/v0.18.0/cmd/workers-assets- gen/assets/common/shim.mjs
Go 側の初期化処理のコード ( 色々省略しています) init 関数の中で、JS 側からGo 側にRequest オブジェクトを渡して処理する handleRequest
関数をglobalThis に設定する func init() { handleRequestCallback := js.FuncOf(func(this js.Value, args []js.Value) any { reqObj := args[0] cb := js.FuncOf(func(_ js.Value, pArgs []js.Value) any { resolve := pArgs[0] go func() { res := handleRequest(reqObj) resolve.Invoke(res) }() return js.Undefined() }) return jsutil.NewPromise(cb) }) jsutil.Global.Set("handleRequest", handleRequestCallback) } https://github.com/syumai/workers/blob/v0.18.0/handler.go
Go 側の初期化処理のコード workers.Serve 関数が呼ばれた時点では既にmain 関数の処理に入っており、init 関数 の処理は終わっているので、JS 側で待ち受けていたPromise を解決する func
Serve(handler http.Handler) { if handler == nil { handler = http.DefaultServeMux } httpHandler = handler jsutil.Global.Call("ready") select {} } https://github.com/syumai/workers/blob/v0.18.0/handler.go
変換処理 Go 側、JS 側でストリームの変換処理が必要 Go のhttp.ResponseWriter -> JS のReadableStream この辺りの話はZenn
に書きました Cloudflare Workers で簡単にGo のHTTP サーバーを動かすためのライブラリを作っ た - https://zenn.dev/syumai/articles/ca9n4e91eqljc44k6ebg
どんなアプリケーションが動くか
gqlgen 製のGraphQL サーバー
gqlgen 製のGraphQL サーバー gqlgen 公式から拾ってきたSTAR WARS server STAR WARS 関連の情報をGraphQL
経由で引っ張るAPI 通常のGo を使っています (2023 年12 月現在、TinyGo では動作しませんでした) https://gqlgen-starwars-example.syumai.workers.dev/ https://github.com/syumai/workers-playground/tree/main/gqlgen-starwars-example こんな感じのクエリを投げると結果が返ってくる query Starships { starship(id: "3003") { id name length history } }
connect-go 製のサーバー 事前に定義したProtobuf をベースに、Buf でconnect-go のコードを生成、サーバーを実 装したもの 通常のGo を使っています (2023
年12 月現在、TinyGo では動作しませんでした) https://emoji.syum.ai message GetEmojiRequest { string short_name = 1; } message Emoji { string short_name = 1; string emoji = 2; } service EmojiService { rpc GetEmoji(GetEmojiRequest) returns (GetEmojiResponse) {} }
connect-go 製のサーバー $ curl -s -H 'Content-Type: application/json' \ https://emoji.syum.ai/emoji.v1.EmojiService/GetEmoji
\ -d '{"short_name": "star"}' | gzip -d | jq . { "emoji": { "shortName": "star", "emoji": " " } } 実装はこちら: https://github.com/syumai/workers-playground/blob/main/connect-go- emoji-server
画像生成サーバー Go 製のsyumai のプロフィールページ TinyGo で動いています もともとGoogle App Engine で動いていたもの
を、Cloudflare Workers に移管した グリーンピースの色をランダムに変化させて表 示したりする https://syum.ai/
画像生成サーバー image/png の利用イメージ import "image/png" func writePNG(w http.ResponseWriter, cMap syumaigen.ColorMap)
{ w.Header().Set("Content-Type", "image/png") img, _ := syumaigen.GenerateImage( syumaigen.Pattern, cMap, 10, ) var buf bytes.Buffer png.Encode(&buf, img) w.WriteHeader(http.StatusOK) io.Copy(w, &buf) } https://github.com/syumai/syum.ai/blob/main/server/image.go
その他 D1 を使ったブログサーバー https://github.com/syumai/workers/tree/main/_examples/d1-blog-server Pages Functions をGo のAPI にする例 https://github.com/syumai/workers/tree/main/_examples/pages-functions
/functions/api/[[routes]].mjs を丸ごとGo にルーティングする形式
活用イメージ Go 製のちょっとしたアプリをデプロイする先として リクエストのあった時しか起動しないので、インスタンスを常時起動するタイプ のサービスを使うより安上がり D1 やKV も使える Scheduled Handler
(Cron Trigger) も使える ( あんまり無いかも) Cloudflare Pages のBackend API として Pages Functions で、普通にGo のAPI サーバーが動かせる
パフォーマンス
パフォーマンス syum.ai (TinyGo 製) を対象に、hey でベンチマークを行った結果を示します 実行タイミングによって、結果にバラつきがある点ご了承ください
静的HTML 配信のベンチマーク (hey https://syum.ai で200req) Summary: Total: 0.2159 secs Slowest:
0.1564 secs Fastest: 0.0164 secs Average: 0.0427 secs Requests/sec: 926.2291 Response time histogram: 0.016 [1] | 0.030 [144] |▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪ 0.044 [5] |▪ 0.058 [5] |▪ 0.072 [2] |▪ 0.086 [0] | 0.100 [0] | 0.114 [26] |▪▪▪▪▪▪▪ 0.128 [10] |▪▪▪ 0.142 [6] |▪▪ 0.156 [1] | Latency distribution: 10% in 0.0182 secs 25% in 0.0197 secs 50% in 0.0216 secs 75% in 0.0508 secs 90% in 0.1131 secs 95% in 0.1256 secs 99% in 0.1304 secs <- 99 percentileで約130ms
画像生成のベンチマーク (hey https://syum.ai/image で200req) Summary: Total: 0.3666 secs Slowest: 0.1868
secs Fastest: 0.0253 secs Average: 0.0608 secs Requests/sec: 545.4886 Response time histogram: 0.025 [1] | 0.041 [127] |▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪ 0.058 [20] |▪▪▪▪▪▪ 0.074 [0] | 0.090 [1] | 0.106 [0] | 0.122 [18] |▪▪▪▪▪▪ 0.138 [23] |▪▪▪▪▪▪▪ 0.154 [1] | 0.171 [7] |▪▪ 0.187 [2] |▪ Latency distribution: 10% in 0.0296 secs 25% in 0.0341 secs 50% in 0.0384 secs 75% in 0.1172 secs 90% in 0.1294 secs 95% in 0.1390 secs 99% in 0.1773 secs <- 99 percentileで約180ms
注意点
Worker のファイルサイズ制限 無料プランだと、圧縮後のサイズが1MB 以内でないといけない制約があるため、バイ ナリサイズが大きくなる通常のGo ではpublish 出来ない TinyGo じゃないと基本的に無理 有料プランなら、10MB
までアップロード可能なので、通常のGo でもある程度のアプ リケーションが動かせる
TinyGo の制限 最近は問題がなくなりつつある 2023 年、ついにencoding/json も動くようになった ただし、複雑なアプリケーションでは、時々ビルドが通らないケースがある ビルドにやや時間がかかる 欠点を補って余りある優位性 (
バイナリサイズの小ささ) があるので、個人的には積極 的に使いたい
今後の展望
TCP sockets 対応 Cloudflare Workers 的には、 cloudflare:sockets の connect 関数でTCP
sockets に 対応している これを使えばTCP 上で動作する任意のプロトコルが使えるようになるので、あらゆる DB が選択肢に入る Go 製のDB ドライバが使えるのは魅力的 コネクションプールの問題も、Hyperdrive が登場したことにより、ある程度解消が見 込めるのでセットで使いたい 実はPR はもらっている 年末年始休暇で何とかします!
ご清聴ありがとうございました!