Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Speaker Deck
PRO
Sign in
Sign up for free
GoでRouter自作実装寄りな話
bmf_san
June 23, 2021
Programming
0
98
GoでRouter自作実装寄りな話
bmf_san
June 23, 2021
Tweet
Share
More Decks by bmf_san
See All by bmf_san
ハイ__ᐛ___パァ_テキストプリプロフェッ__ᐛ___サァ_.pdf
bmf_san
0
27
net/httpでつくるHTTPルーター自作入門
bmf_san
0
88
Golang_chromedp_slack_botでslackの絵文字自動生成ボットをつくってみた.pdf
bmf_san
0
42
GolangでURLルーターをつくった
bmf_san
1
180
Dive to clean architecture with golang
bmf_san
2
980
PHPでURLルーティングを自作する
bmf_san
1
1.7k
ゴリラで学ぶソフトウェアの法則10選
bmf_san
0
170
URLルーティングをつくる
bmf_san
0
1.4k
Laravelで始めるテスト生活
bmf_san
1
810
Other Decks in Programming
See All in Programming
書籍『良いコード/悪いコードで学ぶ設計入門』でエンジニアリングの当たり前を変える
minodriven
3
1k
Node.js 最新動向 TFCon 2022
yosuke_furukawa
PRO
5
2.6k
Nix for Scala folks
kubukoz
0
120
脱オブジェクト指向講座(5分LT資料)
kishida
8
11k
Enterprise Angular: Frontend Moduliths with Nx and Standalone Components @jax2022
manfredsteyer
PRO
0
290
Named Document って何?
harunakano
0
300
Cloud Bigtable を使いこなす秘訣 2022
kusahana
0
230
Reactでアプリケーションを構築する多様化
sakito
4
3.1k
LOWYAの信頼性向上とNew Relic
kazumax55
4
330
Blazor WebAssembly – Dynamische Formulare und Inhalte in Aktion
patrickjahr
0
150
Where and how to run UI tests (Droidcon Lisbon & Android Makers, Paris)
nonews
0
120
コードの解析と言語習得の心得
jinjin33333
0
130
Featured
See All Featured
How GitHub Uses GitHub to Build GitHub
holman
465
280k
Thoughts on Productivity
jonyablonski
43
2.2k
Web Components: a chance to create the future
zenorocha
303
40k
How to train your dragon (web standard)
notwaldorf
57
3.8k
Streamline your AJAX requests with AmplifyJS and jQuery
dougneiner
125
8.5k
Done Done
chrislema
174
14k
The MySQL Ecosystem @ GitHub 2015
samlambert
238
11k
Writing Fast Ruby
sferik
612
57k
I Don’t Have Time: Getting Over the Fear to Launch Your Podcast
jcasabona
12
890
Designing for humans not robots
tammielis
241
23k
Building Better People: How to give real-time feedback that sticks.
wjessup
343
17k
The Mythical Team-Month
searls
208
39k
Transcript
GoでRouter⾃作 実装寄りな話 @bmf_san 2021.06.23 社内LT
なんの話 • net/httpを拡張したrouterのライブラリ • net/httpの”⾜りないところ”を補う • /user/:idのような動的なルーティングができな い
ϒϩάͱ͔-5ͱ͔Ͱ͍·Θͨ͠ωλ
Github https://github.com/bmf-san/goblin
Goblin仕様 • net/http準拠 • 動的なルーティング • 名前付きのパラメータ(パスパラーメーター)をサポート/ users/:id • 正規表現も使える
/users/:id[^\d+$] • ミドルウェアをサポート • net/httpのhttp.Handlerインターフェースに従った関数をミドル ウェアとしてルーティングの処理に組み込める
Goblin例 IUUQTHJUIVCDPNCNGTBOHPCMJOCMPCNBTUFS@FYBNQMFTNBJOHPΛࢀর
有名どころ • ライブラリ • https://github.com/fasthttp/router • https://github.com/julienschmidt/httprouter • http://github.com/go-chi/chi •
FW • https://github.com/gin-gonic/gin • https://github.com/labstack/echo • https://github.com/gorilla/mux
Routerの役⽬ NJEEMFXBSFIBOEMFSͷखલͰॲཧ͞ΕΔΠϝʔδ
Routerの役⽬ • リクエストされたURLやHTTP Methodに応じて、 処理の実⾏を制御するアプリケーション • URLとアプリケーションの処理を結びつける
データ構造を考える ・Router≒⽂字列マッチング ・URLの階層構造と相性が良いデータ構造 →⽊構造
⽊構造
トライ⽊(プレフィックス⽊) ・⽂字列の集合を扱う⽊構造の⼀種 ・ざっくりいうと、⽂字列を探索しやすいように⽊に 格納したやつ ・ex. サジェスト、IPアドレス探索、形態素解析とか ⽂字列を扱う系のやつのベースだったりするぽい
もっと良い⽊ • 時間的計算量(処理時間)、空間的計算量(メモ リ)の効率を追求するなら他に選択肢がある • ex. Radix(Prefix) tree https://www.cs.usfca.edu/ ~galles/visualization/RadixTree.html
• 実装できなかった。ムズい。 • strings.Replacerは内部でRadix tree 実装してい る
トライ⽊を知る • https://www.cs.usfca.edu/~galles/visualization/ Trie.html • アルゴリズムのビジュアライズ • https://github.com/bmf-san/road-to-algorithm- master/tree/master/data_structures/tree/trie •
参照実装
オレオレトライ⽊
オレオレトライ⽊(旧) • 最初のリリースで実装していた⽊ • HTTPメソッドをノードに⼊れてしまうのはアンチパターンだった • ルーティングの処理にHTTPメソッドの⼀致を前提としてしまう • HandlerがHTTPメソッドに依存するような実装になってしまう •
middlewareの対応で不都合に • cf. https://bmf-tech.com/posts/⾃作ルーティングをアップデートした
主な構造体 NBQͱMJOLFEMJTUͷ߹ମͰΛදݱͨ͠Α͏ͳΠϝʔδʁ
Goでrouterを実装する上で知っておきたいこと • net/httpの概観 • どんな構造体があるか、どこを拡張したら良いか、どんなイン ターフェースに従うべきか • cf. https://golang.org/pkg/net/http/ •
HTTPサーバーの処理 • http.ListenAndServeから内部を⾒る • cf. https://bmf-tech.com/posts/GolangのHTTPサーバーのコード リーディング
HTTPサーバーのコードにdeep dive • 良く⾒るGoのHTTPサーバーのコード(⾊々省略さ れている)
HTTPサーバーのコードにdeep dive • 省略しないで書いたパターン
HTTPサーバーのコードにdeep dive • ServeHTTPは関数型のaliasであるHandlerFuncに置き換えることができる • cf. • func (f HandlerfFunc)
ServeHTTP(w ResponseWriter, r *Request) • https://golang.org/src/net/http/server.go?s=64180:64240#L2058
HTTPサーバーのコードにdeep dive • Mux(http.NewServeMux( ))は作らなくても良い。DefaultServeMuxを使うことができ る。 • DefaultServeMuxはServeMux型の構造体を持っており、HandlerFuncというmuxにルー ティングを登録する関数を実装している。 •
cf. • DefaultServeMux • https://golang.org/src/net/http/server.go?s=77627:77714#L2269 • func (mux *ServeMux) HandlerFunc(pattern string, handler func(ResponseWriter, *Request))
HTTPサーバーのコードにdeep dive • Server構造体(http.Server{})もわざわざ作らなくても良い。net/httpには ListenAndServe( )が⽤意されている。 • cf. • func
(*Server) ListenAndServe • https://golang.org/pkg/net/http/#ListenAndServe • func ListenAndServe(addr string, handler Handler) error
Routerを実装するには • →http.Handlerインターフェース を意識して、muxを作れば良い
ルーティングを登録する部分の実装 • cf. https://github.com/bmf-san/goblin/blob/master/router.go • メソッドチェーンでrouteを登録する処理 • ServeHTTPの実装(≒http.Handlerインターフェースの実装)した構造体を作る • ServeHTTP内では、ルーティング、ミドルウェア・ハンドラの実⾏を順番にやる
• 作った構造体はListenAndServeに渡される想定 • cf. https://github.com/bmf-san/goblin/blob/master/_examples/main.go
middleware対応のための実装 • cf. https://github.com/bmf-san/goblin/blob/master/ middleware.go • Sliceに保持したミドルウェアを順番に処理していく • 処理をラップしていくように実⾏する •
cf. https://github.com/bmf-san/goblin/blob/ master/_examples/main.go • いわゆるDecorator patternってやつだと思う
muxにあたる部分の実装 • cf. https://github.com/bmf-san/goblin/blob/master/trie.go • オレオレトライ⽊を頑張ってかく • 単純なトライ⽊を書いて、それを発展させていく • データ構造を考えきった時点で6~7割くらい完成ではある
• テストとデバッガを有効につかう • テストがないと前に進めないくらいテストを有⽤ • ⾊々なパターンのルート定義を考慮する必要があり、テストケースが結構悩ましい • デバッガは⽊の⽣成が上⼿くできているか確認したり、脳内デバッグで間に合わないときに有⽤ • 正規表現の利⽤ • 正規表現を使ったルーティングのケースに対応するためにregexpを使っている • strings.Replacerで代⽤できるならそっちが良い • Goでは正規表現はコストがかかる処理なのでキャッシュして再利⽤性を⾼めると良い • 後で対応しようと思っていたらPRもらえた • https://github.com/bmf-san/goblin/pull/17
ベンチマーク • ϕϯνϚʔΫςετ • cf. https://github.com/julienschmidt/go-http-routing-benchmark • go test -bench=“Goblin|Gin|GorillaMux|HttpRouter|Chi|Beego|Echo”
• ϕϯνϚʔΫ݁Ռ • cf. https://github.com/bmf-san/goblin/issues/ 34#issuecomment-864978325 • Routerબఆͷ؍ύϑΥʔϚϯε͚ͩͰͳ͘ɺػೳ໘net/ httpͷΠϯλʔϑΣʔεΛҙ͍ࣝͯ͠Δ͔Ͳ͏͔ͷόϥϯε͕େࣄ ͦ͏ • cf. https://github.com/julienschmidt/go-http-routing- benchmark#conclusions
ベンチマーク • cf. https://github.com/julienschmidt/go-http-routing- benchmark • GoblinՃ͠·ͨ͠ରઓΑΖ͓͘͠Ͷ͕͍͠·͢PR • https://github.com/julienschmidt/go-http-routing- benchmark/pull/97
• READMEߋ৽͍ͯ͠ͳ͍͔ΒϦετݟͨͧ͠PR • https://github.com/julienschmidt/go-http-routing- benchmark/pull/96