Goでwebサーバー作るときに考えたこと

Goでwebサーバーを作るときに考えたことをまとめておきます。
dockerコンテナで動かす場合を少し考慮してます。

# フルスタックフレームワークか軽量系フレームワークか

最初に突きつけられる問題です。

* フルスタックフレームワーク
  * [revel](https://github.com/revel/revel)
  * [beego](https://github.com/astaxie/beego)
* 軽量系フレームワーク
  * [goji](https://github.com/zenazn/goji)
  * [negroni](https://github.com/codegangsta/negroni) 

どんなフレームワークもそうですが、フルスタックなフレームワークならば、
その道に乗っかることで余計なことを考えずにすみます。
だけどその道から外れることをする場合に苦労が伴います。

golangの場合、Go初心者ならばフルスタックでもいいけれど、
ある程度Goに慣れてくると標準のライブラリといくつかのライブラリを使うだけで
webサーバーを作る場合、十分な場合があります。

前にrevelを使っていたときは、最初はrevelの機能を使っていたけれど、途中で他のライブラリを使ったほうが楽なんじゃないかとなり、少しずつrevelの機能から離れていったことがあります。

今なら自分は、gojiが一番手っ取り早いと思ってます。

なので、ここからは軽量系フレームワークを使った場合の話になります。

# Command Line

* [codegangsta/cli](https://github.com/codegangsta/cli)
* [alecthomas/kingpin](https://github.com/alecthomas/kingpin/)
* [flag](https://golang.org/pkg/flag/)

flagは標準で用意されているライブラリ。
kingpinやcliだとそれに加えて、サブコマンドの設定まで一括で設定することができます。

考慮するべきは

* flagのように1つ1つ設定するか、codegangsta/cliのように一括で設定するか
* 環境変数で設定できるか

dockerを使おうとすると[III. Config - The Twelve-Factor App](http://12factor.net/config)にある通り、環境変数で切り替えしたくなってきます。その場合、[flag](https://golang.org/pkg/flag/)では無理なので、`os.Getenv`の選択肢がでてきます。[codegangsta/cli](https://github.com/codegangsta/cli)と[alecthomas/kingpin](https://github.com/alecthomas/kingpin/)は指定すれば環境変数の値を使うことができます。

`--help`とかで、何が使えるか出てきたほうが便利だよね、と。

[alecthomas/kingpin](https://github.com/alecthomas/kingpin/)のほうが1行ずつ確認できるので個人的には好みです。

# タスクランナー

* Makefile

Goは有能なタスクランナーはないので、Makefileが一番手っ取り早い気がします。`gulp`でも`npm`でも`rake`でも他に乗っかるとかはご自由に。

## webフレームワーク

ウェブフレームワークを選ぶ上で、必要となったのは大きく4つあります。

* http.Handler をサポートしているか
* ServeMux (いわゆるルーティング)
* context
* Graceful

gojiの場合、一通りサポートしてくれているので楽です。
ただ、contextを使うときに`web.C`にロックインされるのが少しネックです。
それが嫌なら [golang.org/x/net/context](https://godoc.org/golang.org/x/net/context)を使えるように考えたほうがいいです。

その場合ルーティング処理と`http.Handler`を分けるために[github.com/julienschmidt/httprouter](https://github.com/julienschmidt/httprouter)や[gorilla/mux](http://www.gorillatoolkit.org/pkg/mux)などのrouterまわりのライブラリの検討が必要になります。

もしくは [kami](https://github.com/guregu/kami) などの検討になりそうです。(さわったことない)

### Context

* [gorilla/context](http://www.gorillatoolkit.org/pkg/context)
* [golang.org/x/net/context](https://godoc.org/golang.org/x/net/context)

[golang.org/x/net/context](https://godoc.org/golang.org/x/net/context)のほうがよく見かける印象があります。

### Gracefull shutdown

* [tylerb/graceful](https://github.com/tylerb/graceful)
* [facebookgo/grace](https://github.com/facebookgo/grace)

両方とも、コード量多くないのでお好きなほうをって感じです。

## Logger

* [logger](http://golang.org/pkg/log/)
* [cihub/seelog](https://github.com/cihub/seelog)
* [siupsen/logrus](https://github.com/Sirupsen/logrus)

ポイントは、エラーレベルの制御とか、log rotationできるかとか、パフォーマンス問題ないかあたりでしょうか。

[siupsen/logrus](https://github.com/Sirupsen/logrus)はlogrotateをアプリケーションレベルでやるべきではないって方針。
[cihub/seelog](https://github.com/cihub/seelog)はlogrotate機能が含まれています。

dockerコンテナで動かす場合、stdout/stderrに流し続けることになりそうなので、あんまり深く考えなくても良いかもです。(参考:[Collecting Docker Container stdout/stderr Logs](http://www.fluentd.org/guides/recipes/docker-logging))

## Template

* [html/template](http://golang.org/pkg/html/template/)
* [flosch/pongo2](https://github.com/flosch/pongo2)

golangの標準は [html/template](http://golang.org/pkg/html/template/) 。

テンプレート内で演算ができないため、複雑な演算系の関数はあらかじめ作成して登録しておく必要があります。

あまりどれも使い勝手が良くないので、マークアップエンジニアと呼ばれる人が書くイメージがつかないです。なので、GoはAPIサーバー作るのは良いけど、ウェブサーバー作るのにはツラいこと多そうな気がしてます。

## テストアサーション

* `go test`
* [stretchr/testify](https://github.com/stretchr/testify)

go testの補助くらいで testify。

# Config

* JSON + 構造体で管理
* [gcfg](https://code.google.com/p/gcfg/)
* [toml](http://github.com/BurntSushi/toml)

上でも書きましたが、Dockerコンテナの場合、環境変数で細かく切り替えできたほうが良さそうなので、コンテナで動かさないときだけ使います。

1回読み込んだら、それを使いまわせるようにするだけなので、configファイルをどの形式で読み込むようにするかの問題になると思います。

# DB Client

DBのクライアントは、

* [database/sql](http://golang.org/pkg/database/sql/)
* 各種サポートDBのdriver https://github.com/golang/go/wiki/SQLDrivers

の2つは必須で、あとはORマッパーやらSQLビルダーを使ったり使わなかったりする選択をしていきます。

上の2つだけだと、ORマッパー的な使い方ができないのもそうですが、結果を取り出してそれを値に詰めるところが面倒だったりします。

参考 [golangでSQLを叩くライブラリまとめ[基本/クエリビルダ/ORM]](http://qiita.com/umisama/items/c022b16101c48ffdbc6a)

例えばPostgreSQLを使う場合、こんな風にいろいろ組み合わせを考えることになります。

* [database/sql](http://golang.org/pkg/database/sql/) + [lib/pq](https://github.com/lib/pq)
* [doug-martin/goqu](https://github.com/doug-martin/goqu)
    * goquは、ORM風 SQLビルダー
* [database/sql](http://golang.org/pkg/database/sql/) + [lib/pq](https://github.com/lib/pq) + [lann/squirrel](https://github.com/lann/squirrel) + [go-gorp/gorp](https://github.com/go-gorp/gorp)
    * squirrel も SQLビルダー
* [jmoiron/sqlx](https://github.com/jmoiron/sqlx) + [lib/pq](https://github.com/lib/pq)
    * sqlxはdatabase/sqlの拡張。Scanが少し便利になってたりする。
* [go-gorp/gorp](https://github.com/go-gorp/gorp)
*  [jinzhu/gorm](https://github.com/jinzhu/gorm) ORM
*  [eaigner/hood](https://github.com/eaigner/hood) ORM
*  [go-xorm/xorm](https://github.com/go-xorm/xorm) ORM

よく見かける[gorp](https://github.com/go-gorp/gorp)は、他のORMとは若干使い勝手が違っていて、
プライマリキーの指定以外でデータ取得したい場合、SQLを書く必要があります。

また、selectのカラムが指定した構造体に無いと[エラーになる](https://github.com/go-gorp/gorp/blob/6d3d64eb828eb0e96a99234d8954e3ad89e2f1d7/gorp.go#L1816)ので、`SELECT *`は利用側で禁止みたいなローカルルールを立てる必要がありそうです。

ORMも、[go-xorm/xorm](https://github.com/go-xorm/xorm)だったら、int8使えないとか、正しくタグを設定しないとincrement後のidが取得できないなど、中身を読まないとハマるポイントがあるので、大きいものを使うならば、それなりの覚悟が必要な気がします。

大きいORMを使わなければ、こんな組み合わせになると思います。

* database/sql (DBアクセス)
* lib/pq (PostgreSQL Driver)
* lann/squirrel (クエリビルダー)
* gorp (結果のScan + Insert, Update, Delete, PK使った処理とか)

## migration

* [liamstask/goose](https://bitbucket.org/liamstask/goose)

あんまり良いのないんです。ほんと。
(参考 [Go製マイグレーションツールの現状確認](https://speakerdeck.com/makotano/gozhi-maiguresiyonturufalsexian-zhuang-que-ren))

## Redis Client

* [garyburd/redigo](https://github.com/garyburd/redigo)

redis公式にいろいろある(http://redis.io/clients#go)ので、好きなのどうぞと。

# Debug

## debugger

* [mailgun/godebug](https://github.com/mailgun/godebug) 最近出てきた。breakpoint打てる。それなりに使えるかも。

## pretty printer

* [davecgh/go-spew](https://github.com/davecgh/go-spew)
* [k0kubun/pp](https://github.com/k0kubun/pp)

pp が色つき。

# まとめ

まだ書いていないものが、いろいろありそうな気がするので、また何か思いついたら追記していきます。