Bootstrap

Redis的各种操作

使用golang语言实现了各种redis的实现

包括初始化redisDb,在服务重启时需要删除的 redis 缓存,对redis里的值进行自增操作并设置过期时间,简单分布式锁,set,get,ttl,delete等等。

哎嘿,你就看吧,一看一个不吱声。

func InitRedisDB(cfg *dbridgeredis.RedisConfig) {
	defer ClearCacheOnServiceRestart()
	client := dbridgeredis.InitRedis(cfg)
	if cfg.ClusterEnable {
		RedisClusterDB = client.(*redis.ClusterClient)
	} else {
		redisDB = client.(*redis.Client)
		injectredis.SetRedis(redisDB)
		injectredis.Inject()
	}
}



// IncrAndExpire 对redis里的值进行自增操作并设置过期时间
func IncrAndExpire(key string, expiration time.Duration) (count int64) {
	c := context.Background()
	currentValues := redisDB.Get(c, key)
	logger.Info(fmt.Sprintf("redis key=%s, currentValues=%v", key, currentValues))
	result := redisDB.Incr(c, key)
	if err := result.Err(); err != nil {
		logger.Error("incr redis key failed", err)
	} else {
		count = result.Val()
		if err := redisDB.Expire(c, key, expiration).Err(); err != nil {
			logger.Error("set redis key expire failed", err)
		}
	}
	return
}

// Set value需要传指针地址
func Set(key string, value interface{}, expiration time.Duration) error {
	c := context.Background()
	if key == "" || value == nil {
		return errors.New("param empty")
	}
	err := hystrixPing()
	if err != nil {
		return err
	}

	data, err := json.Marshal(value)
	if err != nil {
		return err
	}
	err = redisDB.Set(c, key, data, expiration).Err()
	return err
}

func SetNX(key string, value interface{}, expiration time.Duration) error {
	c := context.Background()
	if key == "" || value == nil {
		return errors.New("param empty")
	}
	data, err := json.Marshal(value)
	if err != nil {
		return err
	}
	return redisDB.SetNX(c, key, data, expiration).Err()
}

// SetNXForLock 简单分布式锁
func SetNXForLock(key string, expiration time.Duration) error {
	c := context.Background()
	if key == "" {
		return errors.New("key empty")
	}
	suc := redisDB.SetNX(c, key, 1, expiration)
	if suc.Val() {
		return nil
	}
	return errors.New(locales.TM("common.key_exist", nil))
}

// RedisDistributedLock 方便的在定时任务中使用
func RedisDistributedLock(key string, expiration time.Duration, execFunction func()) (err error) {
	// 设置Redis中的键为分布式锁,如果不存在
	if err = SetNXForLock(key, expiration); err != nil {
		return fmt.Errorf(fmt.Sprintf("Set redis distributed lock error|key=%s|err=", key), err)
	}
	defer func() {
		if err := recover(); err != nil {
			logger.Error(fmt.Sprintf("exec function panic: %v", err), nil)
		}
		// 删除Redis中的键
		err = Delete(key)
		if err != nil {
			logger.Error(fmt.Sprintf("Delete redis distributed lock error|key=%s|err=", key), err)
		}
	}()
	execFunction()
	return
}

// Get ret需要传指针地址
func Get(key string, ret interface{}) error {
	c := context.Background()
	if key == "" {
		return errors.New("param empty")
	}
	err := hystrixPing()
	if err != nil {
		return err
	}

	handle := redisDB.Get(c, key)
	if err := handle.Err(); err != nil {
		return err
	}
	res := handle.Val()
	return json.Unmarshal([]byte(res), &ret)
}

func TTL(key string) (int64, error) {
	c := context.Background()
	if key == "" {
		return -1, errors.New("param empty")
	}
	err := hystrixPing()
	if err != nil {
		return -1, err
	}

	handle := redisDB.TTL(c, key)
	if err := handle.Err(); err != nil {
		return -1, err
	}

	return int64(handle.Val().Seconds()), nil
}

// Delete 删除缓存
func Delete(key string) error {
	c := context.Background()
	if key == "" {
		return fmt.Errorf("empty key when delete")
	}
	err := hystrixPing()
	if err != nil {
		return err
	}
	return redisDB.Del(c, key).Err()
}

func Expire(key string, expiration time.Duration) error {
	return redisDB.Expire(context.Background(), key, expiration).Err()
}

func Scan(cursor uint64, match string, count int64) *redis.ScanCmd {
	return redisDB.Scan(context.Background(), cursor, match, count)
}

func FlushAll() {
	redisDB.FlushDB(context.Background())
}

// Keys 获取指定 key 的所有值,并以切片的形式返回
func Keys(key string) []string {
	c := context.Background()
	var ret []string
	err := hystrixPing()
	if err != nil {
		return ret
	}
	if key == "" {
		return ret
	}
	handle := redisDB.Keys(c, key)
	if err := handle.Err(); err != nil {
		logger.Error("Keys err", err)
		return ret
	}
	res := handle.Val()
	return res
}

// ZAdd 给有序集合中添加元素及分数
func ZAdd(key string, member interface{}, score float64) (int64, error) {
	c := context.Background()
	item := &redis.Z{
		Score:  score,
		Member: member,
	}
	addedCount, err := redisDB.ZAdd(c, key, item).Result()
	if err != nil {
		logger.Error("redis add member to zadd error", err)
		return 0, err
	}
	return addedCount, nil
}

// ZRemRangeByRank 移除有序集合中给定的排名区间内的所有成员
// Redis ZREMRANGEBYRANK 移除有序集key中,指定排名(rank)区间 start 和 stop 内的所有成员。下标参数start和stop都是从0开始计数,
// 0是分数最小的那个元素。索引也可是负数,表示位移从最高分处开始数。例如,-1是分数最高的元素,-2是分数第二高的,依次类推。
func ZRemRangeByRank(key string, start, stop int64) (int64, error) {
	c := context.Background()
	removeCount, err := redisDB.ZRemRangeByRank(c, key, start, stop).Result()
	if err != nil {
		logger.Error("redis remove zadd member error", err)
		return 0, err
	}
	return removeCount, nil
}

// ZRange 获取有序集合中的元素,并对其按照分数进行从大到小排序
func ZRange(key string, start, stop int64) (members []string, err error) {
	c := context.Background()
	members, err = redisDB.ZRange(c, key, start, stop).Result()
	if err != nil {
		logger.Error("redis get zadd error", err)
		return nil, err
	}
	if len(members) > 1 {
		members = utils.ReverseSlice(members)
	}
	return
}

func SAdd(key string, value interface{}) (int64, error) {
	c := context.Background()
	addedCount, err := redisDB.SAdd(c, key, value).Result()
	if err != nil {
		logger.Error("redis setting error", err)
		return 0, err
	}
	return addedCount, nil
}

func SIsMember(key string, value int) (bool, error) {
	c := context.Background()
	existValue, err := redisDB.SIsMember(c, key, value).Result()
	if err != nil {
		logger.Error("查询失败", err)
		return false, err
	}
	return existValue, nil
}

func SIsMemberString(key string, value string) (bool, error) {
	c := context.Background()
	existValue, err := redisDB.SIsMember(c, key, value).Result()
	if err != nil {
		logger.Error("查询失败", err)
		return false, err
	}
	return existValue, nil
}

// Incr 对redis里的值进行自增操作
func Incr(key string) (int, error) {
	err := hystrixPing()
	if err != nil {
		return 0, err
	}
	c := context.Background()
	cmd := redisDB.Incr(c, key)
	return int(cmd.Val()), cmd.Err()
}

// Decr 对redis里的值进行自减操作
func Decr(key string) (int, error) {
	c := context.Background()
	cmd := redisDB.Decr(c, key)
	return int(cmd.Val()), cmd.Err()
}

// DeleteBatch 批量删除缓存
func DeleteBatch(key []string) error {
	err := hystrixPing()
	if err != nil {
		return err
	}
	c := context.Background()
	return redisDB.Del(c, key...).Err()
}

func UnlinkPrefixKeys(prefix string) error {
	err := hystrixPing()
	if err != nil {
		return err
	}
	c := context.Background()
	keys, _ := redisDB.Keys(c, prefix).Result()
	if len(keys) > 0 {
		// unlink 异步删
		err = UnlinkKeys(keys)
		if err != nil {
			logger.Error("redis unlink keys error", err)
		}
	}
	return nil
}

func UnlinkKeys(keys []string) error {
	err := hystrixPing()
	if err != nil {
		return err
	}
	c := context.Background()
	return redisDB.Unlink(c, keys...).Err()
}

func HMget(key string, fields []string) (val []interface{}, err error) {
	c := context.Background()
	return redisDB.HMGet(c, key, fields...).Result()
}

func HSet(key string, field, value interface{}) (res int64, err error) {
	c := context.Background()
	return redisDB.HSet(c, key, field, value).Result()
}

func HGet(key string, field string) (res string, err error) {
	c := context.Background()
	return redisDB.HGet(c, key, field).Result()
}

func HDel(key, field string) (res int64, err error) {
	c := context.Background()
	return redisDB.HDel(c, key, field).Result()
}

func HGetAll(key string) (map[string]string, error) {
	c := context.Background()
	return redisDB.HGetAll(c, key).Result()
}

func HMSet(key string, fields map[string]string) (bool, error) {
	c := context.Background()
	return redisDB.HMSet(c, key, fields).Result()
}

func Lock(lockKey string, expiration time.Duration) (bool, error) {
	var resp *redis.BoolCmd
	for {
		goId := utils.GetCurrentGoroutineId()
		resp = redisDB.SetNX(context.Background(), lockKey, goId, expiration) //返回执行结果
		lockSuccess, err := resp.Result()
		if err == nil && lockSuccess {
			//抢锁成功,开启看门狗 并跳出,否则失败继续自旋
			tls.GoSafe(tls.GetLangLocalStorage(), func() { watchDog(goId, lockKey, expiration) })
			return lockSuccess, err
		}
		//time.Sleep(time.Millisecond * 30) //可以适当休眠
		return lockSuccess, err
	}
}

// 自动续期看门狗
func watchDog(goId int, lockKey string, expiration time.Duration) {
	// 创建一个定时器NewTicker, 每隔2秒触发一次,类似于闹钟
	expTicker := time.NewTicker(time.Second * 2)
	//确认锁与锁续期打包原子化
	script := redis.NewScript(`
    if redis.call('get', KEYS[1]) == ARGV[1]
    then
      return redis.call('expire', KEYS[1], ARGV[2])
    else
      return 0
    end
  `)

	for {
		select {
		case <-expTicker.C: //因为上边是用NewTicker创建的定时器,所以每隔2s都会触发
			resp := script.Run(context.Background(), redisDB, []string{lockKey}, goId, expiration)
			if result, err := resp.Result(); err != nil || result == int64(0) {
				//续期失败
				_, _ = fmt.Printf("expire lock failed:%s", err)
			}
		case <-unlockCh: //任务完成后用户解锁通知看门狗退出
			return
		}
	}
}

func Unlock(lockKey string) error {
	script := redis.NewScript(`
    if redis.call('get', KEYS[1]) == ARGV[1]
    then
      return redis.call('del', KEYS[1])
     else
        return 0
     end
  `)
	resp := script.Run(context.Background(), redisDB, []string{lockKey}, utils.GetCurrentGoroutineId())
	if result, err := resp.Result(); err != nil || result == 0 {
		return fmt.Errorf(fmt.Sprintf("unlock %s failed: %s", lockKey, err))
	}
	//删锁成功后,通知看门狗退出
	unlockCh <- struct{}{}
	return nil
}

// LRange LRange
func LRange(key string, start, stop int64) (members []string, err error) {
	c := context.Background()
	members, err = redisDB.LRange(c, key, start, stop).Result()
	if err != nil {
		logger.Error("redis get LRange error", err)
		return nil, err
	}
	return
}

// RPop RPop
func RPop(key string) (err error) {
	c := context.Background()
	err = redisDB.RPop(c, key).Err()
	if err != nil {
		logger.Error("redis RPop error", err)
		return err
	}
	return
}

// LPush LPush
func LPush(key string, values ...interface{}) (err error) {
	c := context.Background()
	err = redisDB.LPush(c, key, values).Err()
	if err != nil {
		logger.Error("redis LPush error", err)
		return err
	}
	return
}

func LPushValue(key string, value interface{}) error {
	c := context.Background()
	if key == "" || value == nil {
		return errors.New("param empty")
	}
	data, err := json.Marshal(value)
	if err != nil {
		return err
	}
	return redisDB.LPush(c, key, data).Err()
}

func RPopValue(key string, ret interface{}) error {
	c := context.Background()
	if key == "" {
		return errors.New("param empty")
	}
	handle := redisDB.RPop(c, key)
	if err := handle.Err(); err != nil {
		return err
	}
	res := handle.Val()
	return json.Unmarshal([]byte(res), &ret)
}

func AddUniqueLock() (err error) {
	pc, _, _, ok := runtime.Caller(1)
	if !ok {
		logger.Info("AddUniqueLock err")
		return
	}
	funcName := runtime.FuncForPC(pc).Name()

	err = SetNXForLock(fmt.Sprintf("crond_lock:%s", funcName), 3*time.Second)
	return
}

func AddUniqueLockSecTime(sec int64) (err error) {
	pc, _, _, ok := runtime.Caller(1)
	if !ok {
		logger.Info("AddUniqueLock err")
		return
	}
	funcName := runtime.FuncForPC(pc).Name()

	err = SetNXForLock(fmt.Sprintf("crond_lock:%s", funcName), time.Duration(sec)*time.Second)
	return
}

func Exists(key string) (int64, error) {
	c := context.Background()
	return redisDB.Exists(c, key).Result()
}
func SAddNX(key string, value interface{}, expiration time.Duration) (int64, error) {
	c := context.Background()
	addedCount, err := redisDB.SAdd(c, key, value).Result()
	if err != nil {
		logger.Error("redis setting error", err)
		return 0, err
	}
	redisDB.Expire(c, key, expiration)
	return addedCount, nil
}

func BLPop(key string, timeout time.Duration) ([]string, error) {
	err := hystrixPing()
	if err != nil {
		return nil, err
	}
	return redisDB.BLPop(context.Background(), timeout, key).Result()
}

func Watch(key string, function func(tx *redis.Tx) error) error {
	err := hystrixPing()
	if err != nil {
		return err
	}
	return redisDB.Watch(context.Background(), function, key)
}

func RPush(key string, values ...interface{}) (err error) {
	err = hystrixPing()
	if err != nil {
		return err
	}
	return redisDB.RPush(context.Background(), key, values).Err()
}

 

;