使用GAE/Go的自动缓存Datastore库Goon
非常抱歉,我完全忘记了圣诞日历的存在而延误了。给各位相关人员深感抱歉。。。
所以,换句话说。
今年的Advent Calender里涉及很多GAE/Go相关的内容,非常棒。
希望能早日看到去掉”β”字的那一天。
我想简要介绍一下如何使用goon来轻松处理GAE/Go的Datastore。虽然我在以前的Qiita上也提到过它,但我从未见过有人真正使用它…
GAE/Go 的标准 Datastore 包有些小不满。
GAE/Go的Datastore部分非常出色,appengine SDK的datastore包非常好用,只需定义struct就可以非常简单地处理Datastore。
//GAE/Goのdatastore周り
//https://cloud.google.com/appengine/docs/go/datastore/より
type Employee struct {
Name string
Role string
HireDate time.Time
Account string
}
func handle(w http.ResponseWriter, r *http.Request) {
c := appengine.NewContext(r)
e1 := Employee{
Name: "Joe Citizen",
Role: "Manager",
HireDate: time.Now(),
Account: user.Current(c).String(),
}
key, err := datastore.Put(c, datastore.NewIncompleteKey(c, "employee", nil), &e1)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
var e2 Employee
if err = datastore.Get(c, key, &e2); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
fmt.Fprintf(w, "Stored and retrieved the Employee named %q", e2.Name)
}
只是有些烦人的事情。
-
- memcacheへの自動的にキャッシュして欲しい
-
- keyとか特定プロパティから自動生成して欲しい
- Kind名とかType名と同じで良いからわざわざ指定して作りたくない
我有一些小压力。
于是Goon登场了。
小混混
Goon是一个带有自动缓存的GAE/Go Datastore库,它参考了NDB并具有以下特点。
-
- DatastoreのKeyをキャッシュキーとしたin-memoryとmemcacheへの自動キャッシュ
-
- Type名を利用したKind名の自動解決
- Datastore KeyやParent KeyをTypeの特定タグが付けられているフィールドを利用して自動生成
大多数情况下,使用Goon而不是标准的Datastore包,可以使得与Datastore相关的代码变得更短。
使用方法
现在我要写关于使用方法。
在这次的例子中,我想举一个过去为公司开发的工作时间输入应用程序使用的“工作时间”实体。
安装goon
暂时先去拿吧。
$ go get github.com/mjibson/goon
创建类型
首先,我们要创建一个适用于实体(Entity)的类型。
//稼働時間Entity
type Worktime struct {
Ymd string `datastore:"-" goon:"id"`
MemberKey *datastore.Key `datastore:"-" goon:"parent"`
StartTime string `datastore:"startTime,noindex"`
EndTime string `datastore:"endTime,noindex"`
RestTime string `datastore:"lateRestTime,noindex"`
Status WorkStatus `datastore:"status"`
Etc string `datastore:"etc,noindex"`
Worktime int `datastore:"worktime,noindex"`
}
除了常规的 datastore,”Ymd” 字段和 “MemberKey” 字段上都附有 “goon” 标签。
“goon: “id”” 标签声明该字段将用作 datastore.Key 的 Name 值。
“goon: “parent”” 标签声明该字段将用作 datastore.Key 的 Parent Key。
由于它们都不会被保存为 Datastore 实体(在检索时从 Key 获取),因此它们都被标记为 “datastore: -“。
在这种情况下,我们使用MemberKey(表示成员的键)作为Parent Key,并将Ymd(日期)作为Key Name创建datastore.Key,并将其作为Entity的Key。
创建Goon实例
然后创建一个用于进行Get或Put操作的Goon实例。
可以通过*http.Request或appengine.Context来创建Goon实例。
func makeGoon(r *http.Request) goon.Goon {
g := goon.NewGoon(r)
return g
}
如果要在GAE/Go的UnitTest(aetest)等中使用,可以直接从appengine.Context创建。
func Test() error {
if c, err = aetest.NewContext(&aetest.Options{}); err != nil {
panic(err.Error())
}
g = goon.FromContext(c)
}
放置
我想使用上述内容实际将数据存储在Datastore中。
基本的步骤是
-
- 创建Goon实例
-
- 将值填充到目标实体
- 使用Goon实例的Put方法进行Put操作
是的。
func PutWorktime(r *http.Request, memberKey *datastore.Key) (*Worktime, error) {
g := goon.NewGoon(r)
//goonタグの値以外を作る ※サンプルコードのため省略
w, err := createWorktime(r)
if err != nil {
return nil, err
}
//Parent Keyを詰める
w.MemberKey = memberKey
//Datastore KeyのNameを詰める
w.Ymd = "2014-12-24"
//g.PutでPut KeyやKind名はGoon側で解決
//またPut時にMemcacheやIn-Memory Cacheしてくれる
//なお返却値は *datastore.Key, error
if _, err := g.Put(w); err != nil {
return w, err
}
return w, nil
}
通常情况下,需要创建密钥、手动输入种类名称,然后将其放入Memcache中。但是现在这些步骤都被简化了,变得相对清爽。
获得
下一个是Get。
Get基本上只需要抓取带有goon:”id”标签的Feed的值,而不需要考虑datastore.Key。
基本的步骤是
-
- 创建Goon实例
-
- 将Key名称放入目标Entity的goon:”id”字段中
- 通过Goon实例的Get方法进行获取
func GetWorktime(r *http.Request, ymd string, memberKey *datastore.Key) (*Worktime, error) {
g := goon.NewGoon(r)
//goon:"id"とgoon:"parent"(ある場合)に値を詰める
w := &Worktime{
Ymd : ymd,
MemberKey : memberKey
}
//Getは自動的にmemory→memcache→datastoreの順で検索してくれます。
//時々memcacheが恐ろしく遅い場合もある程度のところでtimeoutしてdatastoreを検索しに行ってくれます。
if err := g.Get(w); err != nil {
return nil, err
}
return w, nil
}
使用GetMulti函数可以同时对多个键进行获取操作,并且它本身也支持从缓存中获取数据的功能。
这是Datastore中的一项经典技巧,
当我们像这次一样将“日期”作为键使用,并且想进行日期范围搜索时(比如获取2014年5月的所有数据),
与使用笨拙的datastore.Query进行索引搜索相比,我们可以创建包含该时间段内所有日期的列表,并使用GetMulti进行搜索,
这样我们可以从内存中获取存在于内存的数据,从Memcache中获取存在于Memcache的数据,剩下的则从Datastore中获取,
可以降低搜索成本。
光标
由于代码本身包含在goon文档中,所以我不会再贴出来。但是,即使在使用Cursor的情况下,goon还是能够从缓存中获取已存在的数据,从而减少搜索成本。
总结
Goon是个很棒的工具,它可以很轻松地将GAE经典的Datastore结果存储到Memcache中。由于它与原来的datastore包差异不大,因此迁移也相对轻松。
虽然开发本身不太活跃,但似乎还在悄悄地进行着。
但由于是一个相对较小的库,所以可能不会有太大的发展?
如果有机会使用GAE/Go,请务必试用一下。