AdTech Challengeで初めてGinに触れて感動した話。
CyberAgentさんのAdTech Challengeに参加してきました。
その時にGinを用いて少し感動した話です。
そもそも筆者はこのハッカソンでGoを初めてちょっと本格的に触れました。
www.cyberagent.co.jp
ハッカソンの内容
何が気になったか
うちのチームは「まぁまずは2000QPSを体感するか」ということで
まず、手始めに受け取ったデータをいい感じにしてDBに打ち込みだけのものを作ってみました。
僕の予想では
かなと思いながら見守っていました。
実際にやってみると何と2の方は問題がありませんでした。
「え???」
ってなりました。
実際にリクエストの処理をし始めるまでに100ms経過していました。
その場では開発時間が短いのもあって
「Ginがいい感じにしてるのだろう。以上!」
って感じで1の方のボトルネックの解決をしていきました。
2の方が気になった点だったので終わった後に少しだけ調べてみました
Ginが何をしていたのか
結論はGinがいい感じにリクエスト等をPoolをしていてくれたおかげでゆっくり処理をしてもタイムアウトした後でもリクエストの中身を処理していました。
今回の場合だと大量のリクエストをDBに格納するのみでした。
そのため、レスポンスはなくても良かったので問題ありませんでした。
Ginは軽いフレームワークだと認識していたのでそこまで手厚くしてくれると思っていなかったので少しびっくりしました。
間違いがあったらごめんなさい。
結論はそういうことだったんですが、とりあえず原因探しでGinのコードを追ってみました。
Engineをみてみるとpool...
あっそれっぽいのがありました。
https://github.com/gin-gonic/gin/blob/v1.2/gin.go#L46
type Engine struct { RouterGroup delims render.Delims secureJsonPrefix string HTMLRender render.HTMLRender FuncMap template.FuncMap allNoRoute HandlersChain allNoMethod HandlersChain noRoute HandlersChain noMethod HandlersChain pool sync.Pool trees methodTrees ・ ・ ・
sync.Poolとはなんぞい。
sync - The Go Programming Language
いい感じにPoolしてくれそうだ。 それでそれで何をPoolしているのだ?
https://github.com/gin-gonic/gin/blob/v1.2/gin.go#L108
func New() *Engine { debugPrintWARNINGNew() engine := &Engine{ RouterGroup: RouterGroup{ Handlers: nil, basePath: "/", root: true, }, FuncMap: template.FuncMap{}, RedirectTrailingSlash: true, RedirectFixedPath: false, HandleMethodNotAllowed: false, ForwardedByClientIP: true, AppEngine: defaultAppEngine, UseRawPath: false, UnescapePathValues: true, trees: make(methodTrees, 0, 9), delims: render.Delims{"{{", "}}"}, } engine.RouterGroup.engine = engine engine.pool.New = func() interface{} { return engine.allocateContext() } return engine }
あっpool.Newしてる。。。
ContextをPoolしてるみたいだ。hmhm。
https://github.com/gin-gonic/gin/blob/v1.2/gin.go#L141
func (engine *Engine) allocateContext() *Context { return &Context{engine: engine} }
あっpool.Get()してる。あっServeHTTPだ。
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
c := engine.pool.Get().(*Context)
c.writermem.reset(w)
c.Request = req
c.reset()
engine.handleHTTPRequest(c)
engine.pool.Put(c)
}
調べた感じこんな感じでした。
いや、さすが並列処理に強そうなGo言語なだけあってサクッとできてますね。
しゅ、シュゴイ。
Goの力の片鱗を体感しました。
もともとchannelでやってたのを変えてたみたいでした。 github.com
ハッカソンの結果
審査員特別賞をいただきました。
ボトルネックを的確に判断して、最小限の構成でいったのが評価されたみたいです。
景品にギンピカAbemaくんをいただきました。