为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

Go
DIEA 2022-12-05 16:52:48
我第一次尝试使用 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。
查看完整描述

1 回答

?
呼如林

TA贡献1798条经验 获得超3个赞

您可以使用redigo/redis#Args.AddFlat将结构转换为 Redis 哈希,我们可以使用redis标签映射值。

package main


import (

  "fmt"


  "time"

  "github.com/gomodule/redigo/redis"

)


type Foo struct {

    Number  int64     `json:"number"  redis:"number"`

    ATime   time.Time `json:"atime"   redis:"atime"`

    AString string    `json:"astring" redis:"astring"`

}


func main() {

  c, err := redis.Dial("tcp", ":6379")

  if err != nil {

    fmt.Println(err)

    return

  }

  defer c.Close()


  t1 := time.Now().UTC()

  var foo Foo

  foo.Number = 10000000000

  foo.ATime = t1

  foo.AString = "Hello"


  tmp := redis.Args{}.Add("id1").AddFlat(&foo)

  if _, err := c.Do("HMSET", tmp...); err != nil {

    fmt.Println(err)

    return

  }


  v, err := redis.StringMap(c.Do("HGETALL", "id1"))

  if err != nil {

    fmt.Println(err)

    return

  }

  fmt.Printf("%#v\n", v)

}

然后更新ATime你可以使用redisHSET


if _, err := c.Do("HMSET", "id1", "atime", t1.Add(-time.Hour * (60 * 60 * 24))); err != nil {

  fmt.Println(err)

  return

}

为了将它取回结构,我们必须做一些reflect魔术


func structFromMap(src map[string]string, dst interface{}) error {

  dt := reflect.TypeOf(dst).Elem()

  dv := reflect.ValueOf(dst).Elem()


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

    sf := dt.Field(i)

    sv := dv.Field(i)

    if v, ok := src[strings.ToLower(sf.Name)]; ok {

      switch sv.Interface().(type) {

        case time.Time:

          format := "2006-01-02 15:04:05 -0700 MST"

          ti, err := time.Parse(format, v)

          if err != nil {

            return err

          }

          sv.Set(reflect.ValueOf(ti))

        case int, int64:

          x, err := strconv.ParseInt(v, 10, sv.Type().Bits())

          if err != nil {

            return err

          }

          sv.SetInt(x)

        default:

          sv.SetString(v)

      }

    }

  }


  return nil

}

最终代码

package main


import (

  "fmt"


  "time"

  "reflect"

  "strings"

  "strconv"


  "github.com/gomodule/redigo/redis"

)


type Foo struct {

    Number  int64     `json:"number"  redis:"number"`

    ATime   time.Time `json:"atime"   redis:"atime"`

    AString string    `json:"astring" redis:"astring"`

}


func main() {

  c, err := redis.Dial("tcp", ":6379")

  if err != nil {

    fmt.Println(err)

    return

  }

  defer c.Close()


  t1 := time.Now().UTC()

  var foo Foo

  foo.Number = 10000000000

  foo.ATime = t1

  foo.AString = "Hello"


  tmp := redis.Args{}.Add("id1").AddFlat(&foo)

  if _, err := c.Do("HMSET", tmp...); err != nil {

    fmt.Println(err)

    return

  }


  v, err := redis.StringMap(c.Do("HGETALL", "id1"))

  if err != nil {

    fmt.Println(err)

    return

  }

  fmt.Printf("%#v\n", v)


  if _, err := c.Do("HMSET", "id1", "atime", t1.Add(-time.Hour * (60 * 60 * 24))); err != nil {

    fmt.Println(err)

    return

  }


  var foo2 Foo

  structFromMap(v, &foo2)

  fmt.Printf("%#v\n", foo2)

}


func structFromMap(src map[string]string, dst interface{}) error {

  dt := reflect.TypeOf(dst).Elem()

  dv := reflect.ValueOf(dst).Elem()


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

    sf := dt.Field(i)

    sv := dv.Field(i)

    if v, ok := src[strings.ToLower(sf.Name)]; ok {

      switch sv.Interface().(type) {

        case time.Time:

          format := "2006-01-02 15:04:05 -0700 MST"

          ti, err := time.Parse(format, v)

          if err != nil {

            return err

          }

          sv.Set(reflect.ValueOf(ti))

        case int, int64:

          x, err := strconv.ParseInt(v, 10, sv.Type().Bits())

          if err != nil {

            return err

          }

          sv.SetInt(x)

        default:

          sv.SetString(v)

      }

    }

  }


  return nil

}

注意: struct字段名与redis标签匹配


查看完整回答
反对 回复 2022-12-05
  • 1 回答
  • 0 关注
  • 96 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信