More Related Content Similar to GAE/GoでWebアプリ開発入門 (20) More from Takuya Ueda (20) GAE/GoでWebアプリ開発入門6. A Tour of Go
■ Web上で行えるチュートリアル
● https://go-tour-jp.appspot.com
● 日本語版もある
● まずはBasicsとFlowControlをやってみよ
う!
10. その他
■ コミュニティ
● Gophers Slack #japan
● Google+ Golang JP
■ Qiita
● Goタグでまとまっている
● Go言語の初心者が見ると幸せになれる場所
■ 書籍
● プログラミング言語Go
● Go in Action
● みんなのGo言語
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
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>
...
16. 手元で動かしてみる
■ ソースコードのクローン
■ ローカルサーバで動かす
$git clone
https://github.com/GoogleCloudPlatform/appengine-g
uestbook-go
$git fetch
$git checkout part4-usingdatastore
$goapp serve
http://localhost:8080
をブラウザで開こう
止める際はCtrl+C
20. hello.go
■ ハンドラ
func handler(w http.ResponseWriter,
r *http.Request) {
fmt.Fprint(w, "Hello, world!")
}
レスポンスを書き込むWriter
GAE/GoとGo標準でやり方は同じ!
リクエスト
レスポンスとして
"Hello, world!"を返している
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. ユーザ情報の取得
■ 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を使っても大丈夫
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で送られた
値を取得できる
33. Datastoreについて
■ Cloud Datastoreとは?
● Googleの提供するKey-Valueストア
■ 各種用語のRDBとの対応
概念 Cloud Datastore RDB
オブジェクトのカテゴリ Kind Table
1つのオブジェクト Entitiy Row
1つのオブジェクト各データ Property Field
オブジェクトに対するユニークなID Key Primary Key
34. Kindと構造体
■ Kindを表す構造体を作る
type Person struct {
ID int64 `datastore:"-"`
Name string `datastore:"name"`
Age int `datastore:"age"`
}
構造体のフィールドが
Kindのプロパティに対応
structタグでDatastore上の
プロパティ名を指定
`datastore:"-"`にすると
Datastoreのプロパティにはしない
structタグを省略すると
フィールド名がそのままプロパティ名になる
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. 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. 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. 結果整合性と強い整合性
■ 結果整合性
● 更新結果が必ずしも即時読み取り処理に反
映されない
● スケールしやすい
■ 強い整合性
● 更新結果が即時次の読み取り処理に反映さ
れる
● スケールしにくい
参考
:https://cloud.google.com/datastore/docs/articles/balancing-
strong-and-eventual-consistency-with-google-cloud-datastor
Cloud Datastoreは
選ぶことができる
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
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. エンティティグループへの更新
■ 祖先キーを指定して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回/秒の制限がある
49. デプロイする
■ デプロイしてみる
■ ダメだったら(一度だけ)
■ 開いてみる
● https://[modulename]-dot-[project-id].app
spot.com
$goapp deploy .
$rm $HOME/.appcfg_oauth2_tokens
$appcfg.py update --noauth_local_webserver .