使用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中。

基本的步骤是

    1. 创建Goon实例

 

    1. 将值填充到目标实体

 

    使用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。

基本的步骤是

    1. 创建Goon实例

 

    1. 将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,请务必试用一下。

bannerAds