自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

在 Go 項(xiàng)目中使用 Redis 的幾個(gè)實(shí)用建議

開(kāi)發(fā) Redis
今天來(lái)聊一聊 Redis,主要是聊一些在 Go 項(xiàng)目中使用go-redis 代碼上的一些建議。

在上代碼之前我還是要廢話幾句,在大家開(kāi)發(fā)需求用到Redis時(shí)一定要多想個(gè)兩分鐘 "我是不是把Redis當(dāng)數(shù)據(jù)庫(kù)用了?" 因?yàn)閿?shù)據(jù)在數(shù)據(jù)庫(kù)和Redis里存兩份就就得考慮它們的一致性怎么維護(hù),賊麻煩,而這個(gè)一致性不做上線后還經(jīng)常會(huì)出BUG,所以不是必要我一般不用Redis。

需要過(guò)期的數(shù)據(jù)肯定是要存Redis的,比如用戶的 token 之類的數(shù)據(jù),否則存在數(shù)據(jù)庫(kù)里還得寫(xiě)定時(shí)任務(wù)來(lái)實(shí)現(xiàn)token過(guò)期刪除的功能 。

PS:Token 別用JWT,最好自己實(shí)現(xiàn)一套,后面會(huì)跟大家聊一些這方面的經(jīng)驗(yàn)。

Redis 客戶端的初始化

Redis 客戶端的初始化,這個(gè)我建議還是在做好的Redis分層里通過(guò) Go 自帶的init 函數(shù)來(lái)實(shí)現(xiàn)初始化,別在整個(gè)項(xiàng)目的main方法里一個(gè)個(gè)調(diào)用自己定制化的 InitRedis 之類的方法去實(shí)現(xiàn)。

這個(gè)有人問(wèn)為什么? 很簡(jiǎn)單因?yàn)镚o的那些個(gè)init函數(shù)是在main方法之前執(zhí)行的,就是被設(shè)計(jì)用來(lái)做初始化工作的。而且我們也不必?fù)?dān)心初始化順序的問(wèn)題,被依賴地最深層次的包會(huì)最先被初始化。

package cache

......

var redisClient *redis.Client

func Redis() *redis.Client {
 return redisClient
}

func init() {
 redisClient = redis.NewClient(&redis.Options{
  Addr:         config.Redis.Addr,
  Password:     config.Redis.Password,
  DB:           config.Redis.DB,
  PoolSize:     config.Redis.PoolSize,
 })

 if err := redisClient.Ping(context.Background()).Err(); err != nil {
  // 連接不上redis 讓項(xiàng)目停止啟動(dòng)
  panic(err)
 }
}

go-redis的客戶端初始化完成后,如果不手動(dòng)執(zhí)行Ping 或者是其他Redis操作的話是不會(huì)真的去連接Redis服務(wù)器的,如果你希望在項(xiàng)目啟動(dòng)時(shí)嘗試連接Redis服務(wù)器,失敗則停止啟動(dòng)。那么就加一個(gè)Ping測(cè)試,連接不上用panic 讓程序直接退出。

 if err := redisClient.Ping(context.Background()).Err(); err != nil {
  // 連接不上redis 讓項(xiàng)目停止啟動(dòng)
  panic(err)
 }

當(dāng)然如果你的程序有Redis連接不上讀數(shù)據(jù)庫(kù)的兜底策略,可以選擇在項(xiàng)目啟動(dòng)的時(shí)候不進(jìn)行Redis連接性的測(cè)試。

Redis Key 的命名Tips

我在項(xiàng)目中被 Redis 搞的頭大最多的情況是,有的人特別喜歡在A項(xiàng)目里緩存了個(gè)什么數(shù)據(jù),然后下游的B項(xiàng)目再去讀這個(gè)數(shù)據(jù),根據(jù)緩存里數(shù)據(jù)的狀態(tài)執(zhí)行不同的邏輯分支。

這個(gè)使用場(chǎng)景沒(méi)問(wèn)題,但是很多時(shí)候Redis 的 Key 攜帶的信息實(shí)在是太少,有的時(shí)候我在項(xiàng)目B里面DEBUG,查問(wèn)題看到從Redis里讀取到的數(shù)據(jù)跟預(yù)想的不一樣,但是我在整個(gè)項(xiàng)目里也沒(méi)發(fā)現(xiàn)這個(gè)緩存從哪存的。 這個(gè)時(shí)候如果你們團(tuán)隊(duì)的微服務(wù)拆地足夠好(bushi,服務(wù)比人還多。。。。。。 會(huì)有當(dāng)場(chǎng)去世的感覺(jué)。

別笑,項(xiàng)目比開(kāi)發(fā)多是真事兒,因?yàn)橐郧?0多人的團(tuán)隊(duì)造了10多個(gè)20多個(gè)項(xiàng)目,現(xiàn)在能給你縮減到5個(gè)人都不是怪事兒。

所以我們?cè)谑褂肦edis的時(shí)候,最好把Key 放在項(xiàng)目里統(tǒng)一的地方進(jìn)行管理,同時(shí)在命名上加上包含業(yè)務(wù)、項(xiàng)目、模塊信息的前綴名,通過(guò)它們?cè)诓閱?wèn)題的時(shí)候我們最起碼能快速定位到緩存是哪個(gè)項(xiàng)目寫(xiě)進(jìn)去的。

存結(jié)構(gòu)化數(shù)據(jù),用String 還是 Hash

用Redis時(shí)還有一個(gè)問(wèn)題,就是很多時(shí)候我們的結(jié)構(gòu)數(shù)據(jù)是JSON序列化后存到 Redis 的 String 類型中去的,Redis中還有Hash類型類似于編程語(yǔ)言里的哈希Map。

那么我們存儲(chǔ)結(jié)構(gòu)數(shù)據(jù)的時(shí)候應(yīng)該存到 String 還是 Hash 中呢?答案是都行—— 僅從代碼層面講,哈哈哈......,但是前提是DAO查詢方法返回做好明確的類型聲明,像下面這樣:

unc SetOrder(ctx context.Context, order *do.Order) error {
 jsonDataBytes, _ := json.Marshal(order)
 redisKey := fmt.Sprintf(enum.REDIS_KEY_ORDER_DETAIL, order.OrderNo)
 _, err := Redis().Set(ctx, redisKey, jsonDataBytes, 0).Result()
 if err != nil {
  log.New(ctx).Error("redis error", "err", err)
  return err
 }

 return nil
}

func GetOrder(ctx context.Context, orderNo string) (*do.Order, error) {
 redisKey := fmt.Sprintf(enum.REDIS_KEY_DEMO_ORDER_DETAIL, orderNo)
 jsonBytes, err := Redis().Get(ctx, redisKey).Bytes()
 if err != nil {
  log.New(ctx).Error("redis error", "err", err)
  return nil, err
 }
 data := new(do.Order)
 json.Unmarshal(jsonBytes, &data)
 return data, nil
}

如果你想從 Redis 層面把數(shù)據(jù)的結(jié)構(gòu)化體現(xiàn)的更好一點(diǎn),那么就用Hash,這里需要注意的是go-redis支持把結(jié)構(gòu)體數(shù)據(jù)直接存到Redis Hash 的前提是要在結(jié)構(gòu)體字段的tag 上攜帶 redis 標(biāo)識(shí)。

這里有官方對(duì)這塊的的解釋。

Playing struct With "redis" tag. type MyHash struct { Key1 string `redis:"key1"`; Key2 int `redis:"key2"` }

HSet("myhash", MyHash{"value1", "value2"})

For struct, can be a structure pointer type, we only parse the field whose tag is redis. 

If you don't want the field to be read, you can use the `redis:"-"` flag to ignore it, or you don't need to set the redis tag. 

For the type of structure field, we only support simple data types: string, int/uint(8,16,32,64), float(32,64), time.Time(to RFC3339Nano), time.Duration(to Nanoseconds ), if you are other more complex or custom data types, please implement the encoding.BinaryMarshaler interface. 

所以我們的數(shù)據(jù)結(jié)構(gòu)必須像下面這樣定義:

type DummyOrder struct {
 OrderNo string `redis:"orderNo"`
 UserId  int64  `redis:"userId"`
}

然后go-redis 才能把數(shù)據(jù)通過(guò)HSET 存到Redis的Hash中,而直接讀取Hash數(shù)據(jù)到比如上面定義的結(jié)構(gòu)體的時(shí)候,需要用到go-redis 提供的HGetAll 和 Scan 方法,同理接受數(shù)據(jù)的結(jié)構(gòu)體的字段也需要在tag中攜帶redis標(biāo)識(shí),不帶這個(gè)標(biāo)識(shí)Scan方法不會(huì)把數(shù)據(jù)填充到字段上。

總結(jié)

Redis的使用Tips上就先講這么多,歡迎大家在評(píng)論區(qū)里補(bǔ)充,另外Go項(xiàng)目中用到redis時(shí)也有人會(huì)選擇用redigo,我在工作時(shí)也用過(guò),不過(guò)都是集成給我的一些老項(xiàng)目,不知道是不是redigo這個(gè)庫(kù)出的時(shí)間更早。

責(zé)任編輯:趙寧寧 來(lái)源: 網(wǎng)管叨bi叨
相關(guān)推薦

2024-11-28 09:54:34

項(xiàng)目架構(gòu)模型

2009-06-24 17:34:58

使用JSF的經(jīng)驗(yàn)

2024-10-06 13:41:25

2018-08-21 09:00:30

Linuxtop命令

2024-12-11 09:13:00

2015-08-03 11:45:37

storyboard

2024-10-17 08:58:31

2013-06-25 09:52:32

GoGo語(yǔ)言Go編程

2024-02-04 00:00:00

Go貨幣接口

2024-04-01 00:00:00

Redis緩存服務(wù)消息隊(duì)列

2023-11-27 19:39:46

Goprotobuf

2010-04-29 12:46:42

Oracle SQL

2018-05-02 09:18:17

Linux技巧嵌入式

2023-11-30 09:00:00

TypeScript開(kāi)發(fā)

2024-09-10 09:05:12

SpringREST并發(fā)

2021-11-29 22:59:34

Go Dockertest集成

2023-10-28 16:22:21

Go接口

2024-07-03 13:03:30

Spring注解項(xiàng)目

2023-05-15 08:32:45

2017-07-04 19:02:17

ReacRedux 項(xiàng)目
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)