Go使用Redis做緩存

思路

編寫config.toml文件作爲配置文件,config.go讀取配置文件信息,redis.go通過配置信息創建redis連接的數據連接池,main.go使用redis連接測試效果

項目結構

在這裏插入圖片描述

關鍵代碼

  • 讀取配置文件
//redis的結構體
type redis struct {
   
   
	Network string `toml:"network"`
	Address string `toml:"address"`
	Auth    string `toml:"auth"`
}

//初始化Conf信息
func init(){
   
   
	_,err := toml.DecodeFile(ConfPath,&Conf)//讀取配置文件並保存到Conf
	if err != nil {
   
   
		log.Fatalf("初始化配置信息失敗,err={%v}",err)
	}
}
  • 定義緩存接口
type CacheStore interface {
   
   
	Get(key string,value interface{
   
   }) error
	Set(key string,value interface{
   
   },expires time.Duration) error
	Add(key string,value interface{
   
   },expires time.Duration) error
	Delete(key string) error
	Replace(key string,value interface{
   
   },expire time.Duration) error
}
  • redis.go實現緩存接口
type RedisStore struct {
   
   
	pool              *redis.Pool
	defaultExpiration time.Duration
}

//新建RedisStore對象
func NewRedisCache(network string,address string,password string,defaultExpiration time.Duration) *RedisStore {
   
   
	pool := &redis.Pool{
   
   
		MaxActive: 512,
		MaxIdle: 10,
		Wait: false,
		IdleTimeout: 3 * time.Second,
		Dial: func() (redis.Conn, error) {
   
   
			c,err := redis.Dial(network,address)
			if err != nil {
   
   
				return nil, err
			}
			if len(password) > 0 {
   
    // 有密碼的情況
				if _,err := c.Do("AUTH",password);err != nil{
   
   
					c.Close()
					return nil,err
				}
			}else {
   
   // 沒有密碼的時候 ping 連接
				if _,err := c.Do("ping");err != nil{
   
   
					c.Close()
					return nil,err
				}
			}
			return c,err
		},
	}
	return &RedisStore{
   
   pool: pool,defaultExpiration: defaultExpiration}
}

func (c *RedisStore) Get(key string,value interface{
   
   }) error{
   
   
	conn := c.pool.Get()
	defer conn.Close()
	reply, err := conn.Do("get", key)
	if err != nil {
   
   
		return err
	}
	if reply == nil{
   
   
		return ErrCacheMiss
	}
	bytes, err := redis.Bytes(reply, err)
	return deserialize(bytes,value)//反序列化操作
}
//保存數據
func (c *RedisStore) Set(key string,value interface{
   
   },expires time.Duration) error{
   
   
	conn := c.pool.Get()
	defer conn.Close()
	return c.invoke(conn.Do, key, value, expires)
}
//添加數據
func (c *RedisStore) Add(key string,value interface{
   
   },expires time.Duration) error{
   
   
	conn := c.pool.Get()
	defer conn.Close()
	b, err := redis.Bool(conn.Do("exists", key))
	if err != nil {
   
   
		return err
	}
	if b {
   
    //如果key 已經存在
		return ErrNotStored
	}else {
   
   
		return c.invoke(conn.Do,key,value,expires)
	}

}
//刪除數據
func (c *RedisStore) Delete(key string) error{
   
   
	conn := c.pool.Get()
	defer conn.Close()
	b, err := redis.Bool(conn.Do("exists", key))
	if err != nil {
   
   
		return err
	}
	if !b {
   
    //key值不存在
		return ErrCacheMiss
	}
	_, err2 := conn.Do("del", key)//刪除key值
	return err2
}
func (c *RedisStore) Replace(key string,value interface{
   
   },expires time.Duration) error{
   
   
	conn := c.pool.Get()
	defer conn.Close()
	b, err := redis.Bool(conn.Do("exists", key))
	if err != nil {
   
   
		return err
	}
	if !b {
   
    //key值不存在
		return ErrCacheMiss
	}
	err = c.invoke(conn.Do, key, value, expires)
	if value ==nil {
   
    //空值不能保存
		return ErrNotStored
	}else {
   
   
		return err
	}
}

func (c *RedisStore) invoke(f func(string, ...interface{
   
   }) (interface{
   
   }, error),
	key string, value interface{
   
   }, expires time.Duration) error {
   
   
	b, err := serialize(value)//序列化操作,序列化可以保存對象
	if err != nil {
   
   
		return err
	}
	if expires > 0 {
   
   
		_, err := f("setex", key, int32(expires/time.Second), b)
		return err
	} else {
   
   
		_, err := f("set", key, b)
		return err
	}
}
  • 數據的序列化和反序列化
//序列化
func serialize(value interface{
   
   }) ([]byte, error) {
   
   
	if bytes, ok := value.([]byte); ok {
   
   
		return bytes, nil
	}

	switch v := reflect.ValueOf(value); v.Kind() {
   
   
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return []byte(strconv.FormatInt(v.Int(), 10)), nil
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		return []byte(strconv.FormatUint(v.Uint(), 10)), nil
	}

	var b bytes.Buffer
	encoder := gob.NewEncoder(&b)
	if err := encoder.Encode(value); err != nil {
   
   //編碼
		return nil, err
	}
	return b.Bytes(), nil
}

//反序列化
func deserialize(byt []byte, ptr interface{
   
   }) (err error) {
   
   
	if bytes, ok := ptr.(*[]byte); ok {
   
   
		*bytes = byt

		return nil
	}

	if v := reflect.ValueOf(ptr); v.Kind() == reflect.Ptr {
   
    // 通過反射得到ptr類型,判斷ptr是指針類型
		switch p := v.Elem(); p.Kind() {
   
   
		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64://符號整型
			var i int64
			i, err = strconv.ParseInt(string(byt), 10, 64)
			if err != nil {
   
   
				return err
			} else {
   
   
				p.SetInt(i)
			}
			return nil

		case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64://無符號整型
			var i uint64
			i, err = strconv.ParseUint(string(byt), 10, 64)
			if err != nil {
   
   
				return err
			} else {
   
   
				p.SetUint(i)
			}
			return nil
		}
	}

	b := bytes.NewBuffer(byt)
	decoder := gob.NewDecoder(b)
	if err = decoder.Decode(ptr); err != nil {
   
   //解碼
		return err
	}
	return nil
}

技術支持
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章