Skip to content

Instantly share code, notes, and snippets.

@ReSTARTR
Last active April 13, 2017 06:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ReSTARTR/8ba59e1affdec30ca99ddaefe48658ac to your computer and use it in GitHub Desktop.
Save ReSTARTR/8ba59e1affdec30ca99ddaefe48658ac to your computer and use it in GitHub Desktop.
Go HTTP Libraries

※本記事は2016/09/12 時点の情報をもとに構成しています

GoのWeb開発のためのフレームワークやライブラリについて、整理してみました。

整理するにあたり、いくつかのコンポーネントの分割単位を把握する必要があります。それを単体で提供しているのももあれば、複数の機能をまとめたツールセット(コンポーネント群)、全部入りのフルスタックまで幅広く存在しています。フルスタックなものは各コンポーネントを独自実装している場合が多いです。DBまでカバーしているのはbeegoくらいで、あとは利用者にお任せという仕様。Ruby on Railsのような巨大なものは存在しないと思います。

ライブラリを使わずに標準パッケージのみで構成するのも選択肢として考慮すべきでしょう。選ぶ基準はさまざまだと思いますので、ここではどのあたりが選択基準となるのかを考える材料を提供できればと思います。

評価指標

各種コンポーネントのマッピング

以下の図は、Web/APIアプリケーションを構築するにあたり、必要な要素をならべたうえで、各ライブラリがどの位置に属するのかを示します。

Libraries of golang for web developpers

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/dockermicro/microなど多数の利用事例があります。2015年12月リリースのgo1.2より少し前から存在しています。
  • labstack/echo/* : 2015/03~. 2015年に登場した新し目のSinatraっぽい軽量フレームワーク。フレームワークとしての位置づけですが、一部サブパッケージ化されたものはライブラリとしても使いやすいと思うのでこちらにカテゴライズしました。

routing & middlewares

routing~middlewares(~handler)までをまとめたものです。いちばんよく目にするのはこのあたりのライブラリ。

julienschmidt/go-http-routing-benchmark(とそのフォーク)に各種のベンチマークが掲載されています。速さだけでみるならgin-gonic/ginlabstack/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~. わりと標準ライブラリに寄り添った設計。

routing(=multi plexer)

いずれも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に匹敵する速度と、それ以上の柔軟性を備える。

middlewares(filter)

Rackミドルウェアのような、ハンドラーの前処理や後処理をするWrapper集です。routerとの相性(インターフェースの一致)を考慮する必要があります。(インターフェースを揃えるためのグルーコードを書けば解決するケースもあります)

context

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対応バージョンで追加される予定。)

graceful

処理中のリクエストを殺さずに安全な終了を行うためのライブラリ。

process management

開発時に便利なライブリロードを可能にするライブラリ。

  • 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対応状況

  • 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の引数でコンテキストを受け取る設計だと何かしら移行の対応が必要です

独自コンテキストを提供している主要なライブラリの対応状況は以下のとおりです。

goji/goji

  • go1.7ブランチで対応がすすんでいます
    • HandleFuncC(ctx, w, r)など特殊なものが消えるだけです
    • goji/goji#30
  • 現在マージ待ちです
  • goji/goji#34

gin-gonic/gin

  • x/net/context.Contextとインターフェースは満たしているが、1.7のcontextは未対応っぽいです

labstack/echo

gorilla/mux

おまけ: revel/revel

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:])
}

参考

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment