亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

使用 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 關注
  • 118 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號