Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

GAE/GoでWebアプリ開発入門

6,644 views

Published on

GDG石巻×GCPUG仙台 Devfest 2016で発表した資料です。
https://gdgishinomaki.connpass.com/event/43815/

Published in: Technology

GAE/GoでWebアプリ開発入門

  1. 1. GAE/Goで Webアプリ開発入門 2016/11/26(土) @GDG石巻×GCPUG仙台 Devfest 2016 The Go gopher was designed by Renee French. The gopher stickers was made by Takuya Ueda. Licensed under the Creative Commons 3.0 Attributions license.
  2. 2. 自己紹介 メルカリ/ソウゾウ 上田拓也 twitter: @tenntenn ■ Go歴 / GAE歴 Go:5〜6年くらい? GAE:最近再開、GCPUG Tokyoのスタッフ ■ 業務 GAE/Goでメルカリアッテを作ってます Goのコミュニティを盛り上げる仕事 Gopherを描く仕事(LINEスタンプ)
  3. 3. アジェンダ ■ Go入門(30分) ● Goの特徴 ● Tour of Goをやってみよう ● Goを学習する方法 ■ GAE/Go入門(2時間) ● 開発環境を用意しよう ● ゲストブックを作ってみよう ● デプロイしてみよう
  4. 4. Go入門
  5. 5. Goの特徴 Googleが開発しているプログラミング言語 ■ 特徴 ● 強力でシンプルな言語設計と文法 ● 並行プログラミング ● 豊富な標準ライブラリ群 ● 周辺ツールの充実 ● シングルバイナリ/クロスコンパイル
  6. 6. A Tour of Go ■ Web上で行えるチュートリアル ● https://go-tour-jp.appspot.com ● 日本語版もある ● まずはBasicsとFlowControlをやってみよ う!
  7. 7. Goを学習する方法
  8. 8. 公式ドキュメント ■ パッケージドキュメント ● https://golang.org/pkg ● 標準パッケージの使い方が書いてある ■ FAQ ● https://golang.org/doc/faq ● なぜGoに◯◯がないのか?など ■ 言語仕様 ● https://golang.org/ref/spec 公式ドキュメントを読もう!!
  9. 9. Playground ■ Go Playground ● http://play.golang.org/ ● Web上でGoを実行できる ● Share機能で、SNSで共有したり質問する
  10. 10. その他 ■ コミュニティ ● Gophers Slack #japan ● Google+ Golang JP ■ Qiita ● Goタグでまとまっている ● Go言語の初心者が見ると幸せになれる場所 ■ 書籍 ● プログラミング言語Go ● Go in Action ● みんなのGo言語
  11. 11. GAE/Go入門
  12. 12. Google App Engine (GAE) ■ Google が提供するPaaS ● 高いスケーラビリティ ● メンテナンスコストが低い ■ スタンダード環境とフレキシブル環境 ● スタンダード環境 ○ 従来からあるGAEの環境、SEとも ○ Go、Java7、Python 2.7、PHPが使える ○ インスタンスの起動が恐ろしく早い ● フレキシブル環境 ○ 旧MVMs、FEとも ○ 実際はGCE上で動いている ○ Go、Java8、Python 2.7/3.4、Node.js、Ruby
  13. 13. SDKのインストール ■ 公式サイトからダウンロード ● https://cloud.google.com/appengine/docs/ go/download ● WindowsはPython2.7も必要 ○ すでにPython 3.x系が入ってる場合は注意 ■ zipファイルの展開 ● ダウンロードしたzipをホーム以下に展開 ■ PATHの設定 ● go_appengineにPATHを通す $ export PATH=$HOME/go_appengine:$PATH
  14. 14. SDKの確認 ■ goappの確認 ■ dev_appserver.pyの確認 ■ appcfg.pyの確認 $goapp version go version go1.6.3 (appengine-1.9.46) darwin/amd64 $dev_appserver.py -h usage: dev_appserver.py [-h] [-A APP_ID] [--host HOST] [--port PORT] ... $appcfg.py -h Usage: appcfg.py [options] <action> ...
  15. 15. ゲストブックを作ろう ■ GAE/Goの公式チュートリアル ● https://cloud.google.com/appengine/docs/ go/getting-started/creating-guestbook ● 1ステップごとに学習できる ● httpパッケージの学習 ● html/templateパッケージの学習
  16. 16. 手元で動かしてみる ■ ソースコードのクローン ■ ローカルサーバで動かす $git clone https://github.com/GoogleCloudPlatform/appengine-g uestbook-go $git fetch $git checkout part4-usingdatastore $goapp serve http://localhost:8080 をブラウザで開こう 止める際はCtrl+C
  17. 17. Hello, World ■ Part1を動かしてみる ■ ファイル構成 $git checkout part1-helloworld $goapp serve $tree . . ├── app.yaml └── hello.go
  18. 18. app.yaml ■ アプリケーションの設定ファイル runtime: go api_version: go1 handlers: - url: /.* script: _go_app すべてのリクエストをGoで処理 ルーティングやログインの有無など を設定することができる
  19. 19. hello.go ■ init関数 func init() { http.HandleFunc("/", handler) } パッケージで最初に呼ばれる関数 /というパターンのパスで来たリクエストを ハンドリングする関数を登録 GAE/Goではmain関数は使用しない
  20. 20. hello.go ■ ハンドラ func handler(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "Hello, world!") } レスポンスを書き込むWriter GAE/GoとGo標準でやり方は同じ! リクエスト レスポンスとして "Hello, world!"を返している
  21. 21. ユーザ情報の取得 ■ Part2を動かしてみる ■ ファイル構成 $git checkout part2-usingusers $goapp serve $tree . . ├── app.yaml └── hello.go
  22. 22. コンテキストの取得 ■ コンテキストの取得 ■ コンテキストとは? ● GAEのAPIを使うのに必要 ● 第1引数に渡すことが多い ● 基本的にラップして情報を付加するので、構 造体のフィールドなどには記録しない c := appengine.NewContext(r) rはリクエスト
  23. 23. コンテキストの種類 ■ appengine.Context ● 初代コンテキスト ● Goの標準とは関係ない ■ golang.org/x/net/context.Context ● Go1.6までのGo準標準のコンテキスト ● 最近のGAEで使用されている ○ google.golang.org/appengine ■ フレキシブル環境対応がされたラッパー ■ context.Context ● Go1.7からのGo標準のコンテキスト ● 現在GAEはGo1.6なので使えない 今回はこれを使う GAEの主流はこれ Goの主流はこれ 時代の流れ
  24. 24. ユーザ情報の取得 ■ Googleのユーザ情報の取得 ■ ログインURLの取得とリダイレクト u := user.Current(c) cはコンテキスト url, err := user.LoginURL(c, r.URL.String()) if err { /* エラー処理 */ } w.Header().Set("Location", url) w.WriteHeader(http.StatusFound) リダイレクト http.Redirectを使っても大丈夫
  25. 25. ログインとリダイレクト handler Google ログイン リダイレクト / ログイン成功 開発環境は開発用の ログインURLが生成される
  26. 26. ユーザ入力を受け付ける ■ Part3を動かしてみる ■ ファイル構成 $git checkout part3-handlingforms $goapp serve $tree . . ├── app.yaml └── hello.go
  27. 27. ユーザ入力を受け付ける ■ リクエストからFormデータを取得する func handler(w http.ResponseWriter, r *http.Request) { v := r.FormValue("myvalue") fmt.Fprint(w, v) } <form action="/post" method="post"> <input type="text" name="myvalue"> <input type="submit" value="post"> </form> Go HTML POSTで送られた 値を取得できる
  28. 28. HTMLの表示 ■ ResponseWriterにHTMLを書き込む func root(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, guestbookForm) } const guestbookForm = ` <html> ... </html> ` HTMLを書き込めばブラウザが HTMLをレンダリングしてくれる
  29. 29. テンプレートエンジンの利用 ■ html/templateを使う ● Go標準のテンプレートエンジン ● text/templateのhtml特化版 ■ テンプレートの生成 ■ テンプレートに埋め込む template.New("sign").Parse(signTemplateHTML) テンプレート名 HTML template.Mustはエラーを panicに変換する関数 signTemplate.Execute(w, r.FormValue("content")) リクエストから貰った値を埋め込む
  30. 30. よく使うテンプレートの記法 ■ その文脈でトップレベルのデータを埋め込む ■ フィールドやメソッド ■ 条件分岐 ■ 繰り返し {{.}} {{.Filed}} {{.Method arg1 arg2}} {{if .}}{{.Filed}}{{else}}NO{{end}} {{range .}}{{.}}{{end} rangeの中の {{.}}は要素になる
  31. 31. データの埋め込み ■ HTMLにリクエストの値を埋め込む const signTemplateHTML = ` <html> <body> <p>You wrote:</p> <pre>{{.}}</pre> </body> </html> ` signTemplate.Execute(w, r.FormValue("content")) ここに埋め込まれる
  32. 32. Datastoreを使う ■ Part4を動かしてみる ■ ファイル構成 $git checkout part4-usingdatastore $goapp serve $tree . . ├── app.yaml ├── hello.go └── index.yaml
  33. 33. Datastoreについて ■ Cloud Datastoreとは? ● Googleの提供するKey-Valueストア ■ 各種用語のRDBとの対応 概念 Cloud Datastore RDB オブジェクトのカテゴリ Kind Table 1つのオブジェクト Entitiy Row 1つのオブジェクト各データ Property Field オブジェクトに対するユニークなID Key Primary Key
  34. 34. Kindと構造体 ■ Kindを表す構造体を作る type Person struct { ID int64 `datastore:"-"` Name string `datastore:"name"` Age int `datastore:"age"` } 構造体のフィールドが Kindのプロパティに対応 structタグでDatastore上の プロパティ名を指定 `datastore:"-"`にすると Datastoreのプロパティにはしない structタグを省略すると フィールド名がそのままプロパティ名になる
  35. 35. Datastoreにデータを保存する ■ Incompleteなキーを作る ■ 保存する const k = "Person" // Kind名 key := datastore.NewIncompleteKey(c, k, nil) 親のキーコンテキスト p := &Person{Name:"A", Age:30} newkey, err := datastore.Put(c, key, p) if err != nil {/*エラー処理*/} p.ID = newKey.IntID() エンティティのIDの入った完全なキー
  36. 36. Datastoreからデータを取得する ■ キーを指定して取得する const k = "Person" // Kind名 nID := 100 sID := "" key := datastore.NewKey(c, k, sID, nID, nil) var p Person err := datastore.Get(c, key, &p) if err != nil {/*エラー処理*/} p.ID = key.IntID() どちらかを指定する 複数一気に取得する場合は GetMultiを使う
  37. 37. Datastoreからデータを取得する ■ クエリで取得する const k = "Person" // Kind名 q := datastore.NewQuery(k) // クエリの作成 q = q.Filter("Age >=", 20) // フィルター q = q.Order("Age") // 並べ替え var ps []*Person keys, err := q.GetAll(c, &p) // すべて取得 if err != nil {/*エラー処理*/} for i := range keys { ps[i].ID = keys[i].IntID() // IDを設定 }
  38. 38. 結果整合性と強い整合性 ■ 結果整合性 ● 更新結果が必ずしも即時読み取り処理に反 映されない ● スケールしやすい ■ 強い整合性 ● 更新結果が即時次の読み取り処理に反映さ れる ● スケールしにくい 参考 :https://cloud.google.com/datastore/docs/articles/balancing- strong-and-eventual-consistency-with-google-cloud-datastor Cloud Datastoreは 選ぶことができる
  39. 39. 結果整合性の概念図
  40. 40. 強い整合性の概念図
  41. 41. Datastoreと結果整合性・強い整合性 ■ Cloud Datastore APIと整合性の関係 Cloud Datastore API エンティティの読み取り インデックスの読み取り グローバルクエリ 結果整合性 結果整合性 キーのみのグローバルクエリ なし 結果整合性 Ancestorクエリ 強い整合性 強い整合性 キーによる検索(get) 強い整合性 なし Ancestorクエリを 使うと強い整合性が実現できる 参考 :http://www.slideshare.net/enakai/google-cloud-datastore-insid eout?ref=https://gcpja.connpass.com/event/44024/presentation
  42. 42. エンティティグループ ■ 強い整合性を実現
  43. 43. Ancestorクエリ ■ 祖先キーを指定してAncestorクエリを実行 // 祖先キーの作成 => guestbookKey() const akind = "Guestbook" const asid = "default_guestbook" akey := datastore.NewKey(c,akind,asid,0,nil) q := datastore.NewQuery("Greeting") q = q.Ancestor(akey).Order("-Date").Limit(10) Ancestorクエリになる=強い整合性
  44. 44. エンティティグループへの更新 ■ 祖先キーを指定してPutを実行 // 祖先キーの作成 => guestbookKey() const akind = "Guestbook" const asid = "default_guestbook" akey := datastore.NewKey(c,akind,asid,0,nil) const kind = "Greeting" key := datastore.NewIncompleteKey(c,kind,akey) newkey, err := datastore.Put(c, key, &g) 同一エンティティグループへの 更新は1回/秒の制限がある
  45. 45. デプロイしてみよう
  46. 46. このハンズオンでは ■ アカウントを作るのを省きます ● Google グループに入って頂きます ● このグループは私のGCPプロジェクトのメン バーに設定されています ● [消しました] あとで消します! SNS拡散はダメ!
  47. 47. GCPのコンソールを開く ■ プロジェクトを開く ● https://console.cloud.google.com ● 初めての場合は同意するを選ぶ ● プロジェクトを開く→さらに表示 ● [project-id]を開く
  48. 48. app.yamlの編集 ■ プロジェクトIDとモジュールなどを設定 application: [project-id] runtime: go api_version: go1 module: モジュール名 version: main handlers: - url: /.* script: _go_app プロジェクトID モジュール名 今回は重複を避けるため モジュール名はtwitterアカウント とかにしておいて下さい バージョン
  49. 49. デプロイする ■ デプロイしてみる ■ ダメだったら(一度だけ) ■ 開いてみる ● https://[modulename]-dot-[project-id].app spot.com $goapp deploy . $rm $HOME/.appcfg_oauth2_tokens $appcfg.py update --noauth_local_webserver .
  50. 50. Thank you! twitter: @tenntenn Qiita: tenntenn connpass: tenntenn

×