More Related Content Similar to Go1.8 for Google App Engine (20) Go1.8 for Google App Engine1. The Go gopher was designed by Renée French.
The gopher stickers was made by Takuya Ueda.
Licensed under the Creative Commons 3.0 Attributions license.
Go1.8 for
Google App Engine
@GDG DevFest Tokyo 2017
2017年10月09日(月)
ハッシュタグ:
#DevFest17 #DevFest_room1
3. ソウゾウ エキスパートチーム
技術をアウトプットするところに技術は集まる
■ エキスパートチームとは?
● 50%以上の時間を技術コミュニティへの貢献に充てる
■ エキスパートチームの役割
● 社内に新しい技術を取り取り込む
● 社外のコミュニティなどを通じて社会へ還元する
■ エキスパートチームの活動
● カンファレンス・勉強会の開催/運営
● 対外的な講演活動
● 執筆、雑誌への寄稿、インタビュー
● 社内外での担当技術の普及推進
3
@tenntenn
担当:Go・GCP
@mhidaka
担当:Android
メンバー
6. Google App Engineとは
■ Google が提供するPaaS
● 高いスケーラビリティ
● メンテナンスコストが低い
■ スタンダード環境とフレキシブル環境
● スタンダード環境
○ 従来からあるGAEの環境、SEとも
○ Go、Java8、Python 2.7、PHPが使える
○ Goはインスタンスの起動が恐ろしく早い
● フレキシブル環境
○ 旧MVMs、FEとも
○ Go、Java8、Python 2.7/3.4、Node.js、Ruby
6
8. Hello, world!
8
package myapp
import "fmt"
import "net/http"
func init() {
http.HandleFunc("/", handler)
}
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Hello, world!")
}
main関数ではなくinit関数
net/httpパッケージを使って
HTTPリクエストを処理
HTTPハンドラ
12. Go1.6 → 1.8で新しくなったこと
12
■ Go1.6 → Go1.7
● contextパッケージが標準になった
● サブテストができるようになった
■ Go1.7 → Go1.8
● sort.Sliceが導入され、型を作る必要がなくなった
● pluginパッケージが導入
13. コンテキスト
■ Contextインタフェース
● Google Cloud Platformの各APIを使うために必要
● Goの標準としては主にゴルーチンのキャンセル処理などに使用
13
type Context interface {
Deadline() (deadline time.Time, ok bool)
Done() <-chan struct{}
Err() error
Value(key interface{}) interface{}
}
15. コンテキストに値を持たせる
■ WithValueで値を持たせる
● 例:キャッシュを充てない
15
type withoutCacheKey struct{}
func WithoutCache(c context.Context) context.Context {
if IsIgnoredCache(c) {
return c
}
return context.WithValue(c, withoutCacheKey{}, struct{}{})
}
func IsIgnoredCache(c context.Context) bool {
return c.Value(withoutCacheKey{}) != nil
}
16. Google App Engineとコンテキスト
■ 例:Datastoreからデータの取得
16
type Person struct {
ID int64 `datastore:"-"`
Name string `datastore:"name"`
Age int `datastore:"age"`
}
const k = "Person" // Kind名
key := datastore.NewKey(ctx, k,"",100, nil)
var p Person
if err := datastore.Get(ctx, key, &p); err != nil { /*エラー処理*/}
p.ID = key.IntID()
第1引数でコンテキストを指定
17. コンテキストの歴史
■ appengine.Context
● 初代コンテキスト
● Goの標準とは関係ない
■ golang.org/x/net/context.Context
● Go1.6までのGo準標準のコンテキスト
● Google App Engine(Go1.6の場合)で使用されている
○ google.golang.org/appengine
■ フレキシブル環境対応がされたラッパー
■ context.Context
● Go1.7からのGo標準のコンテキスト
● 現在Google App EngineはGo1.6なので使えない
● ベータ版としてであればGo1.8が使える
17
時代の流れ
18. サブテスト
■ 子テストを実行するしくみ
● Go1.7から導入された
func TestAB(t *testing.T) {
t.Run("A", func(t *testing.T) { t.Error("error") })
t.Run("B", func(t *testing.T) { t.Error("error") })
}
go test -v sample -run TestAB/A
=== RUN TestAB
=== RUN TestAB/A
--- FAIL: TestAB (0.00s)
--- FAIL: TestAB/A (0.00s)
sample_test.go:10: error
FAIL
exit status 1
FAIL sample 0.007s
サブテストを指定して実行
18
19. テーブル駆動テスト
■ テスト対象のデータを羅列してテストする
var flagtests = []struct {
in string
out string
}{
{"%a", "[%a]"}, {"%-a", "[%-a]"}, {"%+a", "[%+a]"},
{"%#a", "[%#a]"}, {"% a", "[% a]"},
}
func TestFlagParser(t *testing.T) {
var flagprinter flagPrinter
for _, tt := range flagtests {
s := Sprintf(tt.in, &flagprinter)
if s != tt.out {
t.Errorf("Sprintf(%q, &flagprinter) => %q, want %q", tt.in, s, tt.out)
}
}
}
19
20. サブテストとテーブル駆動テスト
func TestIsOdd(t *testing.T) {
cases := []*struct {name string; input int; expected bool}{
{name: "+odd", input: 5, expected: true},
{name: "+even", input: 6, expected: false},
{name: "-odd", input: -5, expected: true},
{name: "-even", input: -6, expected: false},
{name: "zero", input: 0, expected: false},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
if actual := IsOdd(c.input); c.expected != actual {
t.Errorf(
"want IsOdd(%d) = %v, got %v",
c.input, c.expected, actual)}})
}
}
20
22. Go1.7までのソート
22
type byAge []Person
func (a byAge) Less(i, j int) bool { return a[i].Age < a[j].Age }
func (a byAge) Len() int { return len(a) }
func (a byAge) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func main() {
people := []Person{ {"Gopher", 7}, {"Alice", 55}, {"Vera", 24} }
sort.Sort(byAge(people))
}
■ sort.Interfaceを実装する必要があった
23. sort.Sliceの導入
23
func main() {
people := []Person{{"Gopher", 7}, {"Alice", 55}, {"Vera", 24}}
sort.Slice(people, func(i, j int) bool {
return people[i].Age < people[j].Age
})
}
■ 型を作る必要がなくなった
32. GAE/Go1.8のコンテキストの管理法
32
■ Contextの中にApp Engineのコンテキストを入れる
● リクエストをキーに使っていない
● Request.WithContextされてもコンテキストはコピーされる
const ContextKey keyType = "App Engine context"
func NewContext(req *http.Request) context {
c := req.Context().Value(ContextKey)
if c == nil { /* 省略 */}
return c.(context)
}
goroot-1.8/src/appengine_internal/api_go18.go
33. コンテキストの置き換え
33
■ go tool fixコマンドを使う
● Goのバージョン間のマイグレーションを行うコマンド
● Go1.0以前に活躍
● x/net/context.Contextからcontext.Contextに置き換える
● goappコマンドにはない
$ go tool fix -force=context main.go
import文をsedしても対して変わらない
34. 型の不一致の問題
■ 型が合わないケースが存在する
● コンテキストを含むコンポジット型
○ マップやスライス
● コンテキストを引数や戻り値に取るクロージャ
34
err := datastore.RunInTransaction(ctx, func(ctx context.Context) error {
key := datastore.NewKey(ctx, "Counter", "singleton", 0, nil)
var cnt struct { Count `datastore:"count"` }
err := datastore.Get(ctx, key, &cnt)
if err != nil && err != datastore.ErrNoSuchEntity { return err}
cnt.Count++
if _, err := datastore.Put(ctx, key, &cnt); err != nil { return err }
}, nil)
datastore.RunInTransactionの例
35. 型の不一致の解決
■ ラッパーを作る
● ライブラリに合わせてクロージャの型を変換する
35
import xcontext "golang.org/x/net/context"
func RunInTransaction(c context.Context, f func(c context.Context) error) error {
return datastore.RunInTransaction(c, func(xc xcontext.Context) error {
return f(xc)
})
}
datastore.RunInTransactionのラッパー
37. まとめ
■ Go1.8にはベータとして対応
● 大きな問題は解決済み
● サブテストやsort.Sliceが使えるのは大きい
● Goはバージョンアップごとにパフォーマンスが改善している
■ 本番利用にはまだ早そう
● コンテキストの問題が残っている
● 正式版に向けてライブラリ等の改修がされる?
● メルカリ カウルではChat Opsから導入中
● Go1.9はよ
37