猿问

使用 Golang、redis 和时间进行测试

我第一次尝试使用 Redis 进行一些测试,但遇到了一些与HGET/ HSET/的混淆HGETALL。我的主要问题是我需要存储时间,并且我想使用哈希,因为我会不断更新时间。


起初我读到这样的MarshalBinary功能如何拯救我:


func (f Foo) MarshalBinary() ([]byte, error) {

    return json.Marshal(f)

}

它所做的是将结构保存为 json 字符串,但仅保存为字符串,而不是实际的 Redis 哈希。我最终做的是一个相当大的样板代码,它使我想要保存到地图中的结构,并且该结构作为哈希正确存储在 Redis 中。


type Foo struct {

    Number int       `json:"number"`

    ATime  time.Time `json:"atime"`

    String string    `json:"astring"`

}


func (f Foo) toRedis() map[string]interface{} {

    res := make(map[string]interface{})

    rt := reflect.TypeOf(f)

    rv := reflect.ValueOf(f)

    if rt.Kind() == reflect.Ptr {

        rt = rt.Elem()

        rv = rv.Elem()

    }

    for i := 0; i < rt.NumField(); i++ {

        f := rt.Field(i)

        v := rv.Field(i)

        switch t := v.Interface().(type) {

        case time.Time:

            res[f.Tag.Get("json")] = t.Format(time.RFC3339)

        default:

            res[f.Tag.Get("json")] = t

        }

    }

    return res

}

完整的代码在这里

我在想一定有更明智的方法来解决这个问题?还是我被迫做这样的事情?我需要存储的结构只包含整数、字符串和 time.Times。

*edit 评论字段有点短,所以改为编辑:

我最初确实像评论中建议的“傻瓜”一样解决了它并作为答案。我更改为上述部分的原因是,虽然解决方案更复杂,但我认为它对于更改更健壮。如果我使用硬编码地图解决方案,我“必须”拥有:

  • 带有字段散列键的常量,因为它们至少会在两个地方使用(来自和到 Redis),所以它会成为编译器未发现的愚蠢错误的地方。当然可以跳过,但知道我自己的拼写很可能会发生

  • 如果有人只是想添加一个新字段并且不太了解代码,它会编译得很好但是不会在 Redis 中添加新字段。一个容易犯的错误,尤其是对于有点天真的初级开发人员或过于自信的高级开发人员。

  • 我可以将这些辅助函数放在一个库中,当需要时间或复杂类型时,我们所有的代码都会神奇地工作。

不过,我打算提出的问题/希望是:我真的必须像这样跳过箍才能使用 go 将时间存储在 Redis 哈希中吗?公平,time.Time 不是原始数据,Redis 也不是(非)sql 数据库,但我认为缓存中的时间戳是一个非常常见的用例(在我的例子中是一个心跳,用于跟踪超时会话和元数据足以永久存储它,因此需要更新它们)。但也许我滥用了 Redis,我宁愿有两个条目,一个用于数据,一个用于时间戳,这样我就可以使用两个简单的获取/设置函数获取 time.Time 和返回 time.Time。


DIEA
浏览 97回答 1
1回答

呼如林

您可以使用redigo/redis#Args.AddFlat将结构转换为 Redis 哈希,我们可以使用redis标签映射值。package mainimport (&nbsp; "fmt"&nbsp; "time"&nbsp; "github.com/gomodule/redigo/redis")type Foo struct {&nbsp; &nbsp; Number&nbsp; int64&nbsp; &nbsp; &nbsp;`json:"number"&nbsp; redis:"number"`&nbsp; &nbsp; ATime&nbsp; &nbsp;time.Time `json:"atime"&nbsp; &nbsp;redis:"atime"`&nbsp; &nbsp; AString string&nbsp; &nbsp; `json:"astring" redis:"astring"`}func main() {&nbsp; c, err := redis.Dial("tcp", ":6379")&nbsp; if err != nil {&nbsp; &nbsp; fmt.Println(err)&nbsp; &nbsp; return&nbsp; }&nbsp; defer c.Close()&nbsp; t1 := time.Now().UTC()&nbsp; var foo Foo&nbsp; foo.Number = 10000000000&nbsp; foo.ATime = t1&nbsp; foo.AString = "Hello"&nbsp; tmp := redis.Args{}.Add("id1").AddFlat(&foo)&nbsp; if _, err := c.Do("HMSET", tmp...); err != nil {&nbsp; &nbsp; fmt.Println(err)&nbsp; &nbsp; return&nbsp; }&nbsp; v, err := redis.StringMap(c.Do("HGETALL", "id1"))&nbsp; if err != nil {&nbsp; &nbsp; fmt.Println(err)&nbsp; &nbsp; return&nbsp; }&nbsp; fmt.Printf("%#v\n", v)}然后更新ATime你可以使用redisHSETif _, err := c.Do("HMSET", "id1", "atime", t1.Add(-time.Hour * (60 * 60 * 24))); err != nil {&nbsp; fmt.Println(err)&nbsp; return}为了将它取回结构,我们必须做一些reflect魔术func structFromMap(src map[string]string, dst interface{}) error {&nbsp; dt := reflect.TypeOf(dst).Elem()&nbsp; dv := reflect.ValueOf(dst).Elem()&nbsp; for i := 0; i < dt.NumField(); i++ {&nbsp; &nbsp; sf := dt.Field(i)&nbsp; &nbsp; sv := dv.Field(i)&nbsp; &nbsp; if v, ok := src[strings.ToLower(sf.Name)]; ok {&nbsp; &nbsp; &nbsp; switch sv.Interface().(type) {&nbsp; &nbsp; &nbsp; &nbsp; case time.Time:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; format := "2006-01-02 15:04:05 -0700 MST"&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ti, err := time.Parse(format, v)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return err&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; sv.Set(reflect.ValueOf(ti))&nbsp; &nbsp; &nbsp; &nbsp; case int, int64:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; x, err := strconv.ParseInt(v, 10, sv.Type().Bits())&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return err&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; sv.SetInt(x)&nbsp; &nbsp; &nbsp; &nbsp; default:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; sv.SetString(v)&nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }&nbsp; }&nbsp; return nil}最终代码package mainimport (&nbsp; "fmt"&nbsp; "time"&nbsp; "reflect"&nbsp; "strings"&nbsp; "strconv"&nbsp; "github.com/gomodule/redigo/redis")type Foo struct {&nbsp; &nbsp; Number&nbsp; int64&nbsp; &nbsp; &nbsp;`json:"number"&nbsp; redis:"number"`&nbsp; &nbsp; ATime&nbsp; &nbsp;time.Time `json:"atime"&nbsp; &nbsp;redis:"atime"`&nbsp; &nbsp; AString string&nbsp; &nbsp; `json:"astring" redis:"astring"`}func main() {&nbsp; c, err := redis.Dial("tcp", ":6379")&nbsp; if err != nil {&nbsp; &nbsp; fmt.Println(err)&nbsp; &nbsp; return&nbsp; }&nbsp; defer c.Close()&nbsp; t1 := time.Now().UTC()&nbsp; var foo Foo&nbsp; foo.Number = 10000000000&nbsp; foo.ATime = t1&nbsp; foo.AString = "Hello"&nbsp; tmp := redis.Args{}.Add("id1").AddFlat(&foo)&nbsp; if _, err := c.Do("HMSET", tmp...); err != nil {&nbsp; &nbsp; fmt.Println(err)&nbsp; &nbsp; return&nbsp; }&nbsp; v, err := redis.StringMap(c.Do("HGETALL", "id1"))&nbsp; if err != nil {&nbsp; &nbsp; fmt.Println(err)&nbsp; &nbsp; return&nbsp; }&nbsp; fmt.Printf("%#v\n", v)&nbsp; if _, err := c.Do("HMSET", "id1", "atime", t1.Add(-time.Hour * (60 * 60 * 24))); err != nil {&nbsp; &nbsp; fmt.Println(err)&nbsp; &nbsp; return&nbsp; }&nbsp; var foo2 Foo&nbsp; structFromMap(v, &foo2)&nbsp; fmt.Printf("%#v\n", foo2)}func structFromMap(src map[string]string, dst interface{}) error {&nbsp; dt := reflect.TypeOf(dst).Elem()&nbsp; dv := reflect.ValueOf(dst).Elem()&nbsp; for i := 0; i < dt.NumField(); i++ {&nbsp; &nbsp; sf := dt.Field(i)&nbsp; &nbsp; sv := dv.Field(i)&nbsp; &nbsp; if v, ok := src[strings.ToLower(sf.Name)]; ok {&nbsp; &nbsp; &nbsp; switch sv.Interface().(type) {&nbsp; &nbsp; &nbsp; &nbsp; case time.Time:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; format := "2006-01-02 15:04:05 -0700 MST"&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ti, err := time.Parse(format, v)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return err&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; sv.Set(reflect.ValueOf(ti))&nbsp; &nbsp; &nbsp; &nbsp; case int, int64:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; x, err := strconv.ParseInt(v, 10, sv.Type().Bits())&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if err != nil {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return err&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; sv.SetInt(x)&nbsp; &nbsp; &nbsp; &nbsp; default:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; sv.SetString(v)&nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }&nbsp; }&nbsp; return nil}注意: struct字段名与redis标签匹配
随时随地看视频慕课网APP

相关分类

Go
我要回答