※本記事は2016/09/12 時点の情報をもとに構成しています
GoのWeb開発のためのフレームワークやライブラリについて、整理してみました。
整理するにあたり、いくつかのコンポーネントの分割単位を把握する必要があります。それを単体で提供しているのももあれば、複数の機能をまとめたツールセット(コンポーネント群)、全部入りのフルスタックまで幅広く存在しています。フルスタックなものは各コンポーネントを独自実装している場合が多いです。DBまでカバーしているのはbeego
くらいで、あとは利用者にお任せという仕様。Ruby on Railsのような巨大なものは存在しないと思います。
ライブラリを使わずに標準パッケージのみで構成するのも選択肢として考慮すべきでしょう。選ぶ基準はさまざまだと思いますので、ここではどのあたりが選択基準となるのかを考える材料を提供できればと思います。
以下の図は、Web/APIアプリケーションを構築するにあたり、必要な要素をならべたうえで、各ライブラリがどの位置に属するのかを示します。
1つの図ですべてをカバーできないため、おもに多数派を優先して構成しています。特定のライブラリの表現が適切でないことも多いので参考程度に見てください。
Go1.7からcontextが標準化されたことにより、コンテキストへのアクセス方法が赤い矢印の流れに変わる可能性があります。ハンドラーに対する引数が何なのか次第で対応が変わってきますが、それぞれどのように変更されるかは注目すべき点だと思います。
1.7以降はサポートしないで開発打ち切りになるライブラリもあるかもしれませんし、1.8リリースの頃には各ライブラリは1.7以前をサポート外にする可能性もありそうな気がします。(echoを例に見るとcontext/context_1.7.goのようなビルドタグ指定がつらそうなので...)
図には含めませんでしたが、これ以外に必要なものとして以下も挙げられます。
- configuration: 環境ごとの設定切替など
- testing: テストのサポート
- monitoring: middlewareとして提供されることが多い
これらの分割単位を把握しておくことで、雨後の筍のように出てくるライブラリにも惑わされず冷静に評価できるようになるかと思います。
※記載している日付はREADMEの初回コミット日時です。
Goの標準ライブラリを完全に隠蔽するような形式なので、フレームワーク上での実装におけるサポートは手厚いです。でもそれが窮屈に感じる可能性もあり、そもそもそこまで必要なの?という疑問を常に抱くことになるはず...最近はこれに近いフレームワークは目立って出てきていないと思います。go1.0が2012年03月にリリースされましたが、その前後に生まれたフレームワークですので、相当歴史が長いです。
- revel/revel: 2011/12~. 従来のMVC構造(controllers,models,viewsのディレクトリ)
- astaxie/beego: 2012/12~. ORMまで入っている全部入り. 中国のユーザー多い。
Web/APIアプリケーションに必要なパッケージをまとめて提供してくれるものです。別リポジトリだったり、1リポジトリだったり、提供方法は異なります。
- gorilla/* : 2013/10~. 比較的古参なライブラリ群。
docker/docker
やmicro/micro
など多数の利用事例があります。2015年12月リリースのgo1.2より少し前から存在しています。 - labstack/echo/* : 2015/03~. 2015年に登場した新し目のSinatraっぽい軽量フレームワーク。フレームワークとしての位置づけですが、一部サブパッケージ化されたものはライブラリとしても使いやすいと思うのでこちらにカテゴライズしました。
routing~middlewares(~handler)までをまとめたものです。いちばんよく目にするのはこのあたりのライブラリ。
julienschmidt/go-http-routing-benchmark(とそのフォーク)に各種のベンチマークが掲載されています。速さだけでみるならgin-gonic/gin
かlabstack/echo
あたりになりそうです。routingのみのライブラリとくらべて、Handlerのインターフェースが異なるものが多いです。
- go-martini/martini : 2013/10~. ※開発終了
- gocraft/web : 2013/11~. ちょっと古め。
- gin-gonic/gin : 2014/06~. Martini作者のcodegangsta氏が開発。
- goadesign/goa: 2015/10~. Praxisインスパイアされた系。microservices構築を意識したライブラリ。
- goji/goji : 2015/11~. わりと標準ライブラリに寄り添った設計。
いずれもhttp.ServeMux
に代わるルーターを提供するライブラリ。ミドルウェア機構を備えているか、Handlerを呼び出すインターフェースはhttp.Handler
互換か、などが評価ポイントになります。
- net/http : 標準ライブラリ(
http.ServeMux
)。/item/:id
のようなプレースホルダ機能などを提供しないシンプルなルーター。 - gorilla/mux : 2012/10~.
http.ServeMux
互換。わりと歴史あるもの。 - julienschmidt/httprouter : 2013/12~ :
http.ServeMux
互換。gin-gonic/gin
でもコピーして利用されているライブラリ。(importではない:issue#328) - dimfeld/httptreemux: 2014/05~.
goadesign/goa
でも採用されているルーター。julienschmidt/httprouter
に匹敵する速度と、それ以上の柔軟性を備える。
Rackミドルウェアのような、ハンドラーの前処理や後処理をするWrapper集です。routerとの相性(インターフェースの一致)を考慮する必要があります。(インターフェースを揃えるためのグルーコードを書けば解決するケースもあります)
- gorilla/handlers : 2013/02~.
gorilla/mux
などと組み合わせて使うミドルウェア。 - urfave/negroni : 2014/05~ :
http.ServeMux
と組み合わせて使うミドルウェア。サードパーティー製ミドルウェアも豊富。 - labstack/echo/middleware : 2015/03~.
labstack/echo
用ミドルウェア。 - goadesign/goa/middleware : 2015/10~.
goadesign/goa
用ミドルウェア。goa.NewMiddleware内部でhttp.Handler
やhttp.Handler
を引数に取るようなミドルウェアも利用可能な柔軟性をもつ。
Go1.7で標準化されたことにより、標準以外を選択するケースは少ないかもしれません。が、labstack/echo
のようにライブラリによっては独自Contextに依存せざるを得ない場合もあります。Contextの担う責務がそれぞれ異なっているのが注意点。
- context : go1.7から標準化された汎用パッケージです。httpにおいては
http.Request
経由で読み書きを行います。 - net/context : go1.6までのスタンダードでした。依存しているライブラリも多いので当面は保守されていくと思われます。
- gorilla/context : 2012/10~. go1.7以降は使う必要ありません。
- (labstack/echo/context : 2015/03~. 現時点のバージョンには存在せず、1.7対応バージョンで追加される予定。)
処理中のリクエストを殺さずに安全な終了を行うためのライブラリ。
- grace/gracehttp : 2012/01~2015/08.
- zenazn/goji/graceful : 2014/03~.
goji/goji
の旧バージョンですが、リポジトリは今でもメンテされています。 - tylerb/graceful : 2014/05~.
開発時に便利なライブリロードを可能にするライブラリ。
- codegangsta/gin : 2013/07~. 前述の
gin-gonic/gin
とは名前は同じでも機能は別物です。 - pilu/fresh : 2014/01~.
これまで紹介したライブラリには基本的にデータベース関連の機能は(beegoを除いて)含まれていません。ディレクトリ構成なども利用者が自由に考えることが出来ます。
- データベースオブジェクト(
*sql.DB
)をどこに保持するか- ハンドラーの属するパッケージグローバルな変数?
- 別のデータリポジトリ層のパッケージグローバルな変数?
少なくとも、user
とかitem
とかパッケージをわけて、そのなかにハンドラーやリポジトリを構成するのが良いのではないでしょうか。
|
|--- item/
| |--- handler.go
| |--- handler_test.go
| |--- repo.go
| |--- repo_test.go
| `--- templates/
|--- user/
| |--- handler.go
| |--- handler_test.go
| |--- repo.go
| |--- repo_test.go
| `--- templates/
|--- middlewares/
| |--- authenticator.go
| |--- authenticator_test.go
| |--- monitor_test.go
| `--- monitor.go
|--- routes.go
`--- server.go
が、今回はスコープ外ということで深追いしないでおきます。。。
- go1.7からは
http.Request
経由でアクセス可能ですfunc (r *http.Request) Context() context.Context
: contextの読み出しfunc (r *http.Request) WithContext(ctx context.Context) *http.Request
: 新規contextの生成
- そのため今後はハンドラーからは
http.Request
にアクセスできる手段があれば良いことになります - ミドルウェアでコンテキストを注入することも可能です
- とはいえ、公式にサポートしているかはもともとの設計によります
- このコンテキストをデータベースアクセスにも使う場合に型の不一致が生じる可能性があります
- Handlerの引数でコンテキストを受け取る設計だと何かしら移行の対応が必要です
独自コンテキストを提供している主要なライブラリの対応状況は以下のとおりです。
- go1.7ブランチで対応がすすんでいます
HandleFuncC(ctx, w, r)
など特殊なものが消えるだけです- goji/goji#30
- 現在マージ待ちです
- goji/goji#34
- x/net/context.Contextとインターフェースは満たしているが、1.7のcontextは未対応っぽいです
- gin.Contextへのグルーコード書けばOK...? : gin-gonic/gin#654
- gin-gonic/gin#684
- buildタグで1.7対応しています(
// +build go1.7
と// +build !go1.7
) - masterにもマージされているので、次リリースには取り込まれそうな雰囲気です。
- 対応がすすんでいます
- そもそもcontext対応していない独自機構
- https://github.com/revel/revel/blob/v0.13.1/server.go#L23-L41
- だから、Filterでタイムアウトコンテキストとかつくってリクエストオブジェクト化してやればよさげ?
func TimeoutFilter(c *Controller, fc []Filter) {
ctx := c.Request.Request.Context()
ctx = ctx.WithTimeout(ctx, 1 * time.Second)
c.Request.Request = c.Request.Request.WithContext(ctx)
fc[0](c, fc[1:])
}
- Golang でのウェブ開発を考えてみる - Qiita
- goa: Untangling Microservices: gojiを採用しなかった理由など。