golang基于go-redis实现分布式锁

项目最近涉及到自动弹缩,因此涉及到分布式锁相关的处理;
项目redis采用 go-redis库(点击跳转)进行操作;因此基于此实现了一个分布式锁,做一个记录;
如有缺陷,欢迎指正

  1. redis分布式锁的实现是基于 SETNX 命令的特性来完成的, 即:仅首次设置值时才能够设置成功
    同时做了一定的保护,防止恶意不设置过期时间导致永久占用锁的情况。
  2. 释放锁时,使用了MULTI/EXEC ,同时 WATCH 锁保证不会错误的释放锁;
package fwRedis

import (
	"errors"
	"fmt"
	"github.com/go-redis/redis/v7"
	"github.com/satori/go.uuid"
	"leiax00.com/fxWeb/util"
	"time"
)

func GetLock(lockName string, acquireTimeout, lockTimeOut time.Duration) (string, error) {
	code := uuid.NewV4().String()
	endTime := util.FwTimer.CalcMillis(time.Now().Add(acquireTimeout))
	for util.FwTimer.CalcMillis(time.Now()) <= endTime {
		if success, err := fwRedisClient.SetNX(lockName, code, lockTimeOut).Result(); err != nil && err != redis.Nil {
			return "", err
		} else if success {
			return code, nil
		} else if fwRedisClient.TTL(lockName).Val() == -1 { //-2:失效;-1:无过期;
			fwRedisClient.Expire(lockName, lockTimeOut)
		}
		time.Sleep(time.Millisecond)
	}
	return "", errors.New("timeout")
}

//var count = 0  // test assist
func ReleaseLock(lockName, code string) bool {
	txf := func(tx *redis.Tx) error {
		if v, err := tx.Get(lockName).Result(); err != nil && err != redis.Nil {
			return err
		} else if v == code {
			_, err := tx.Pipelined(func(pipe redis.Pipeliner) error {
				//count++
				//fmt.Println(count)
				pipe.Del(lockName)
				return nil
			})
			return err
		}
		return nil
	}

	for {
		if err := fwRedisClient.Watch(txf, lockName); err == nil {
			return true
		} else if err == redis.TxFailedErr {
			fmt.Println("watch key is modified, retry to release lock. err:", err.Error())
		} else {
			fmt.Println("err:", err.Error())
			return false
		}
	}
}

完整代码实例 ==> please click me

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