gae/gを動かす
今日はAppengine上で動く第3の言語であるGoについてです
僕がgae/goしたい理由は以下の3つです
spin-upがきちがいじみて早い
実行速度そのものも早い (並行処理を言語レベルでサポートしてくれている)
Javaより書くのが楽しそう
以上のような理由から、gae/g楽しそうだなーと思ってます
experimental早く外れないかなー
gae/gを動かすには、goをインストール必要があります
インストール方法は公式を見ても良いし
日本語なら、Go言語のインストール - golang.jpを見るのも良いと思います
また、ymotongpoo / goenv — Bitbucketを使うのも良いかも
※gae/gのSDKの中にgoがあるから、go installしなくても良いんじゃないかと教えてもらいました
さて、goがインストールできたら、gae/gをSDKをdownloadします
Download and Install the SDK for App Engine | App Engine Documentation | Google Cloud
僕の環境はMacなので、go_appengine_sdk_darwin_amd64-1.7.7.zipをdownloadしました
こいつを適当な場所に解凍します
僕の環境では、以下に解凍しました
~/bin/google_appengine
それでは、適当な何かを作っていきます
今回は横山さんの本に出てくる簡単なguest bookを作って見ることにしました
しかし、それには試練があります
本が出た後に、golangとgaeのversionが上がっていて、サンプルがそのままでは動かない・・・!
ということで、version上がって変わった辺りを適当にクリアしながら、進んでいきます
まず、作業用のディレクトリを作ります
~/bin/google_appengine/apps/gae-g-book
このディレクトリの中にファイルを作っていきます
最初にAppengineの設定ファイルであるapp.yamlを書きます
app.yaml
application: gae-g-book version: 2013-01-27 runtime: go api_version: go1 handlers: - url: /style\.css static_files: html/style.css upload: html/style.css - url: /.* script: _go_app
gae/pをしている人には、お馴染みにファイルなのかも
僕はgae/jしかしたことがないので、よく分からないけど
gae/jでは、appengine-web.xmlに相当するファイルです
このままでは、よく分からないので、解説を入れてみました。
application: gae-g-book // application名 version: 2013-01-27 // appengine にデプロイする時に使うversion runtime: go // go言語を使うので、go api_version: go1 // go version 1を使うので、go1 handlers: // cssファイルをstatic fileとして登録している - url: /style\.css static_files: html/style.css upload: html/style.css - url: /.* // 全部のURLをgoで処理する script: _go_app
こんな感じです
そして、実際にリクエストが来た時の処理が以下
main.go
package process import ( "appengine" "appengine/datastore" "fmt" "log" "net/http" "text/template" "time" ) // Datastoreにぶっこむために使う type Guest struct { Name string Date time.Time } // 画面表示用に使う type Guest_View struct { Name string Date string } // リクエストが来た時のルーティング func init() { http.HandleFunc("/", handler) http.HandleFunc("/write", write) http.HandleFunc("/list", list) } const inputForm = ` <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>名前の登録</title> </head> <body> <form method="POST" action="write"> <label>お名前<input type="text" name="name" /></label> <input type="submit"> </form> </body> </html> ` const guestTemplateHTML = ` <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>登録者リスト</title> </head> <body> <table border="1"> <tr> <th>名前</th> <th>登録日時</th> </tr> {{range .}} <tr> {{if .Name}} <td>{{.Name|html}}</td> {{else}} <td>名無し</td> {{end}} {{if .Date}} <td>{{.Date|html}}</td> {{else}} <td> - </td> {{end}} </tr> {{end}} </table> </body> </html> ` var guestTemplate = template.Must(template.New("guest").Parse(guestTemplateHTML)) // htmlをそのままResponseにぶっこむ func handler(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "%s", inputForm) } // submit された値を、Datastoreに入れる func write(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { w.WriteHeader(http.StatusNotFound) w.Header().Set("Content-Type", "text/plain; charset=utf-8") fmt.Fprintf(w, "Not Found") return } c := appengine.NewContext(r) // Datastoreへの書き込み var g Guest g.Name = r.FormValue("name") g.Date = time.Now() if _, err := datastore.Put(c, datastore.NewIncompleteKey(c, "Guest", nil), &g); err != nil { http.Error(w, "Internal Server Error : "+err.Error(), http.StatusInternalServerError) return } http.Redirect(w, r, "/", http.StatusFound) } // Datastoreに入っている値の一覧を表示 func list(w http.ResponseWriter, r *http.Request) { c := appengine.NewContext(r) q := datastore.NewQuery("Guest").Order("Date") count, err := q.Count(c) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } guests := make([]Guest, 0, count) if _, err := q.GetAll(c, &guests); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } log.Printf("guests len=%d", len(guests)) guest_views := make([]Guest_View, count) for pos, guest := range guests { guest_views[pos].Name = fmt.Sprintf("%s", guest.Name) localTime := guest.Date.Format("2006/01/02 15:04:05") guest_views[pos].Date = localTime } if err := guestTemplate.Execute(w, guest_views); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } }
簡単ですが、こんな感じです
こいつを、dev serverで動かしてみます
cd ~/bin/google_appengine/apps
dev_appserver.py gae-g-book
これで、dev serverが動きます
INFO 2013-04-14 13:49:51,111 dispatcher.py:150] Starting server "default" running at: http://localhost:8080
INFO 2013-04-14 13:49:51,113 admin_server.py:117] Starting admin server at: http://localhost:8000
上記のようなメッセージが表示されて、dev server と admin serverのURLが分かります
因みに僕が2月ぐらいに試した時は、admin serverはhttp://localhost:8080/_ah/adminとかだった気がしたけど、変わった?
以上で、今回おしまいです
細かいところの説明は、飛ばしちゃったりしているので、分からないことがあったらコメントかtwitterで聞いちゃってください
僕もまだgae/gは触り始めたばかりですが、面白そうなので、これからも触っていきたいと思います
UnitTestはどうすれば良いんだ?とか、mavenみたいなのあるのかな?とか、まだまだ疑問はありますが、少しずつ・・・
今回のソースはGithubに上げてあります