読者です 読者をやめる 読者になる 読者になる

GoでWebアプリケーション作る練習をした

GoでWebアプリケーションを書いてみる練習として RequestBin ぽいものを試しに作ってみた。gomibakoという名前であまりひねりはない。以下のURLで試せます。

https://gomibako.douzemille.net/

ソースコードもGitHubに公開してある。

github.com

何ができるか

HTTPリクエストを受け付ける用のURLを作ることができて、そのURLに対するHTTPリクエストのログをWeb上で確認することができる。ちょっとしたWebHookの動きのチェックとかリバースプロキシの設定確認とかに使えて便利。

具体的には以下の様にして使える

  1. https://gomibako.douzemille.net/ にアクセスして "New Gomibako" ボタンを押す
  2. https://gomibako.douzemille.net/g/deadbeaf123/inspect みたいなURLが作られてリダイレクトされる。このページにログが表示される。
  3. 画面に表示されている、https://gomibako.douzemille.net/g/deadbeaf123 みたいなURLに別のタブやcurlとかでHTTPリクエストを送る
  4. 2で表示したページに今アクセスしたHTTPリクエストが表示される

ログ画面は以下のような感じになる。

f:id:hakobe932:20161125120132p:plain

HTTPアクセスがあるとリアルタイムにログが更新されるようになっていてるのが、ちょっとおもしろい。TwitterにURLをながすといろんなクローラが次々とやってくるのがリアルタイムに楽しめる。

なんか怖いのでHTTPリクエストのログは永続化していない。URLごとに最新10リクエストしか保持しないし、URLを作ってから一時間経つとそのURLは破棄されて使えなくなる。

ちょっとリクエストの様子を調べられるところが欲しかったので、自分としては満足してる。これまではリクエストログをダンプするだけのちっちゃいWebアプリケーション(app.psgi 3行くらい)を使ってたけど、いまいち使いづらかった。

技術的見どころ

習作という感じであまり見どころはないけど、せっかくなので紹介します。

Webアプリケーションの構成するGoのライブラリ

GoのWebアプリケーションフレームワークは多種多様で選択肢は豊富なものの、Goの net/http パッケージがしっかりしているので、あんまり分厚いフレームワークは使わずに済ませたほうがシンプルで良さそう。ということで最低限のライブラリで構成してる。

  • httptreemux
    • さすがに net/http のルーティングは機能が貧弱なのでつかってる
    • HTTPルータの実装もいろいろあるけど、速度やメモリフットプリントのパフォーマンスを優先して機能が足りないのも多い
    • httptreemuxはまぁまぁバランスが良さそう
  • negroni
    • 一番うすそうなミドルウェア実装かな、と思って選んだ
    • ミドルウェアのインターフェースも独自の型とか使ってなくてリーズナブルな感じ
  • secure
    • セキュリティ関係のヘッダをいい感じにつけてくれる汎用のミドルウェア

これくらいでだいたい困ってないし、とくていのライブラリへの依存度も高くないので結構いいんではと思ってる。セッションとか、データベースへのコネクションとかのグローバルステートの管理とかしたくなってくると、もうすこしいろいろ入れたくなってくるのかもしれない。もっと複雑なGoのWebアプリケーション開発している人の知見も知りたい

GoによるSever Sent Event の実装

リアルタイムにHTTPのログをページに出すために Server Sent Event を使ってる。Goの net/http はevent-streamのエンドポイントもちゃんと実装できるようになっている。

実際のコードはこのあたり:
https://github.com/hakobe/gomibako/blob/master/main.go#L148-L194

HTTPのハンドラの最後で無限ループを作って、その中でeventを書き込むようにしてしまえばコネクションは切断されない。ブラウザ側にもevent-streamってわかるヘッダをはいてやればコネクションは維持される。ただし、リバースプロキシがコネクションをタイムアウトさせたりということはあるので、コネクションが途中できれても良いようにはする。

コネクションははりっぱなしなので、書き込みバッファリングのフラッシュのタイミングはある程度制御してやる必要がある。http.ResponseWriterをキャストして、http.Flusherを作ってeventの書き込みごとにフラッシュしてやると良い。

おなじくhttp.ResponseWriterをキャストして、http.CloseNotifierのオブジェクトを取得すると、HTTPコネクションが切れたときに何かしらの処理をすることができる。

コネクションの管理はchannelを使ってがんばってやってる。リクエストを受け付けるURLに対応するオブジェクトが直接channelを管理するという雰囲気になっていて迫力がある(このあたりのコード >
https://github.com/hakobe/gomibako/blob/master/lib/gomibako.go#L32 )。mutex使っててこのへんのコードはむずくなっている。

channelはGoにおいてはファーストクラスのオブジェクトだけど、ドメインレイヤで扱うのはよくなさそうな感触。かなり実装の都合に依存するので、インフラストラクチャレイヤで管理したほうが良さそう。まぁこのプロジェクトでは、リポジトリっぽいレイヤしかなくてごちゃごちゃしている。interfaceとかおしゃれに使いたい。

まとめ

GoでWebアプリケーション書く練習をしてみて何かしら便利なものはできた。また、界隈にどういうフレームワークあったりするのかとか、net/httpの便利具合などはよくわかった。

一方、DBにリクエストしてテンプレートにいろいろレンダリングするみたいな、普通っぽいWebアプリケーションを題材に選ばなかったので、DBの管理の感じとかレイヤの設計とかあんまり気にせずにベロっとつくってしまった。Sever Sent Event を実装するところとかを主にがんばったけど、その知見の使い所は少なそう。interfaceをいいかんじに使って関心事を分離したい人生だった。

次は日記システムとかを作るといいんだろうかな。GoでWebアプリばりばり作ってる人いたら知見教えて欲しい。