Go言語にはいろいろなWebフレームワークが存在して、はっきりとしたデファクトスタンダードが決まっていません。
しいて言えば標準パッケージの net/http
がデファクトですが、世の中ではそこに機能不足を感じた人たちが多くのフレームワークを開発しています。
そこで、いくつかのフレームワークを取り上げて、簡単なベンチマークと、それぞれのフレームワークでのいわゆるHello Worldの書き方をまとめておきます。
これによって、フレームワーク選びの参考になればと思います。
対象
Revel、Beego、Kochaなど、見かけたが入れていないものがいくつかあります。コマンドでスケルトンを作るもの、net/http
の Handler interface を満たさないものは除外しました。
追加してくれる方はコメントか編集リクエストをお願いします!
Negroni あたりは入れたいですね。
個人的な感想
後半を読み飛ばしたい人のために、個人的な好みでオススメをあげておきますと、GinかEchoです。パフォーマンスも良く、いろいろ便利メソッドもあるのでよさそうです。
もっと薄いものが欲しい人は net/http
が良いでしょう。
ベンチマーク
GET /gopher
のリクエストを送って Hello, gopher!
を返す単純なコードを書いて計測します。
ベンチマークは https://github.com/najeira/go-frameworks-benchmark にあります。フレームワークを追加したい方、ベンチマークの種類を増やしたい方、ぜひ Pull Request をお願いいたします。
なお、大量のハンドラがある場合のルーティングについて知りたい人は https://github.com/julienschmidt/go-http-routing-benchmark も参照してください。
時間
ns/op | |
---|---|
Gin | 734 |
Echo | 879 |
Kami | 1237 |
Goji | 1299 |
Bone | 1463 |
http | 1854 |
Gocraft | 1915 |
Gorilla | 3944 |
Martini | 5823 |
GinとEchoが優秀です。Martiniは内部でリフレクションを使っているためか遅いですが、それでもナノ秒の単位ですので、ここがアプリケーションのボトルネックになることはなさそうです。
allocs
allocs/op | |
---|---|
Gin | 3 |
Echo | 5 |
Goji | 6 |
Bone | 7 |
http | 7 |
Kami | 9 |
Gocraft | 12 |
Gorilla | 15 |
Martini | 22 |
ここでもGinとEchoが優秀です。とはいえ、GoのGCは進歩して性能が上がっていること、アプリケーション内でのアロケーションが支配的になるでしょうから、ここも大きな差はないといってよいでしょう。
GitHub
Stars | Contributors | |
---|---|---|
Martini | 7995 | 93 |
Gin | 4857 | 64 |
Goji | 2888 | 17 |
Echo | 2698 | 28 |
Gorilla | 1808 | 27 |
Gocraft | 918 | 11 |
Bone | 823 | 11 |
Kami | 127 | 4 |
Martiniは歴史もあり、初期に多くのStarsを獲得、またContributeも多いようです。次いでGin。また、Echoは2015年スタートですが、かなりの勢いでStarsを獲得しています。
コード
コード全体は https://github.com/najeira/go-frameworks-benchmark を見てください。ここでは雰囲気を把握してもらえれば。
Bone
m := bone.New()
m.Get("/:name", handler)
func handler(w http.ResponseWriter, r *http.Request) {
name := bone.GetValue(r, "name")
fmt.Fprintf(w, "Hello, %s", name)
}
Echo
m := echo.New()
m.Get("/:name", handler)
func handler(c *echo.Context) error {
name := c.Param("name")
return c.String(200, "Hello, %s", name)
}
Gin
m := gin.New()
m.GET("/:name", handler)
func handler(c *gin.Context) {
name := c.Param("name")
c.String(200, "Hello, %s", name)
}
Goji
m := goji.New()
m.Get("/:name", handler)
func handler(c goji.C, w http.ResponseWriter, r *http.Request) {
name := c.URLParams["name"]
fmt.Fprintf(w, "Hello, %s", name)
}
Gocraft
m := gocraft.New(ctx{})
m.Get("/:name", (*ctx).handler)
func (c *ctx) handler(w gocraft.ResponseWriter, r *gocraft.Request) {
name := r.PathParams["name"]
fmt.Fprintf(w, "Hello, %s", name)
}
Gorilla
m := goriila.NewRouter()
m.HandlerFunc("/{name}", handler)
func handler(w http.ResponseWriter, r *http.Request) {
name := gorilla.Vars(r)["name"]
fmt.Fprintf(w, "Hello, %s", name)
}
Kami
m := kami.New()
m.Get("/:name", handler)
func handler(c context.Context, w http.ResponseWriter, r *http.Request) {
name := kami.Param(c, "name")
fmt.Fprintf(w, "Hello, %s", name)
}
Martini
m := martini.Classic()
m.Get("/:name", handler)
func handler(w http.ResponseWriter, p *martini.Params) string {
name := p["name"]
return fmt.Sprintf("Hello, %s", name)
}