プログラミングをする気力が戻るくらいまで回復してきたので、久しぶりにポスト。

新たにアプリケーションを作るにあたってGAE Goを試してみたメモ。最近正式版になったようだ。GAEではPythonを主に使用し、Javaのライブラリを使用したい時に補完的にJavaを使用した経験があるが、Goは初めて。

環境構築

OSX前提。

SDK

SDKを公式サイトからダウンロードし、適当なディレクトリに展開。ここではホームディレクトリー直下に置いた。gcloud componentsとは独立しているようで、gcloud components update appなどしてもインストールはできない。

PATHも通しておく。

export PATH=$HOME/go_appengine:$PATH

https://cloud.google.com/appengine/downloads#Google_App_Engine_SDK_for_Go

GOPATH

GOPATHをまだ設定していない時は、設定しておく。

export GOPATH=$HOME/go

go-lang-idea-plugin

自分はWebStormを使っているので、Intelij IDEAのGo用のPluginを入れる。

https://github.com/go-lang-plugin-org/go-lang-idea-plugin/wiki/Documentation

セットアップはこの通りにやれば問題なかった。説明にあるように、まだAlpha版でIntelijのリポジトリには公開されていないので注意。

チュートリアルをやっている時にstandard libraryのimportが重複していると表示されて混乱したが、これはプラグインの設定で最初はGO SDK欄にはHomebrewから入れたGoのlibexecを指定していたためで、go_appengine SDKのgorootを指定すると問題なく動いてくれている模様。

/Users/<username>/go_appengine/goroot

Tutorialで動作確認

https://cloud.google.com/appengine/docs/go/gettingstarted/helloworld

GAE Pythonの知識が流用できるので理解が楽。

goapp serveでdev_serverを起動する。コードの変更を検知してコンパイルしてくれるので、コンパイルを気にせずにブラウザーのリロードで変更が反映されるのが楽ちんで良い。

goapp serveの時には、dev_appserver.pyを内部で呼び出しているが、$PATHの設定によってはPython GAEを動かすgcloudの方のdev_appserverが呼び出されてしまって実行に失敗していたので、$PATHの設定を調整して解決した。

ライブラリとか

Flask的なフレームワークを探してみた。

Negroni

http://negroni.codegangsta.io/

フレームワーク探しの結果、Negroniを試した。Martiniでも良かった気がするが、MartiniはReflectionを使っていてそれがコードを追いにくくしていること、実行効率を下げていることがGoらしくないと批判されていて、作者がそれを受けて作ったのがNegroniとのこと。せっかくなのでこちらを使ってみた。フレームワークでなくHttp Middlewareと作者は言っている。Interfaceを決めてmiddleware stackを重ねていく。良くあるスタイル。コードがコンパクトで追いやすいのが癒される。 Appengineに組み込むと、たぶんこうなる。

import (
	"net/http"
	"fmt"
	"appengine"
	"github.com/codegangsta/negroni"
	"github.com/goincremental/negroni-sessions"
	"github.com/goincremental/negroni-sessions/cookiestore"
)

func init() {
	n := negroni.New()
	store := cookiestore.New([]byte("secret123"))
	n.Use(sessions.Sessions("my_session", store))

	mux := http.NewServeMux()
	mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
		session := sessions.GetSession(req)
		session.Set("hello", "world!")
		fmt.Fprintf(w, "Welcome to the home page!!")
	})
	mux.HandleFunc("/hello", func(w http.ResponseWriter, req *http.Request) {
		session := sessions.GetSession(req)
		hello := session.Get("hello").(string)
		fmt.Fprintf(w, hello)
	})
	n.UseHandler(mux)

	http.Handle("/", n)
}

SignUp/SignIn/SignOut時のsession操作はここから自分で書く必要があるようだ(特に難しくはないが)。またGoogle Sign inに対応させたいのだが、Go版のサンプルコードが無かったり、Google API client libraryがまだAlpha版だったりする。

作ろうとしているアプリケーションの要件では、Go channelを使う予定はないしPythonでなにも問題はないし既に書いたコードはあるしで、このあたりで我にかえってきた。

その他、ライブラリはここらへんから良さそうなのを選べば良いようだ。

振り返り

  • Goの文法はシンプルで明快なのでとても読みやすい。
  • スクリプトを書くように気楽に書ける。
  • コンパイル速い。とくにコンパイル時間を意識しなくて良い。
  • GAEの経験があるなら、Datastore等の設計の知識を流用できるので、ライブラリ選定や習熟の学習コストは低い。
  • 将来的にはPythonからの移行を検討する。

なんでGAE?

楽。PaaSなので、制約を理解して設計すれば運用コストが劇的に下がるため。Datastoreの設計は学ぶ価値がある。どうしてもRDBを使わなければならない局面があれば、CloudSQLを使えば良い。

GAE(というかGCP)はいろいろな機能を提供してくれるリッチなプラットフォームなので、Django的な大抵はRDB前提な全部入りフレームワークよりも、Flaskのようなマイクロフレームワークと組み合わせるほうがすっきりして良いと感じている。

Python版は2.7系で3系は未対応なので、unicodeの扱いを考慮する必要があってそれをつい忘れがちで面倒なのだが、そのへんは割り切る方向で。おそらく3系を使いたかったらManaged VMにしてねという方向になっていくんだろうか。はやくManaged VMが安定してほしい。docker_lessなものに切り替えていくようで、GAEのシンプルさが好きなのでその方向性は歓迎。まあManaged VMが安定したときはDart appengineに乗り換えているかもしれない。