mirror of
				https://gitee.com/dromara/mayfly-go
				synced 2025-11-04 08:20:25 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			97 lines
		
	
	
		
			2.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			97 lines
		
	
	
		
			2.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package rediscli
 | 
						|
 | 
						|
import (
 | 
						|
	"context"
 | 
						|
	"mayfly-go/pkg/global"
 | 
						|
	"mayfly-go/pkg/utils/stringx"
 | 
						|
	"time"
 | 
						|
 | 
						|
	"github.com/redis/go-redis/v9"
 | 
						|
)
 | 
						|
 | 
						|
const LockKeyPrefix = "mayfly:lock:"
 | 
						|
 | 
						|
// RedisLock redis实现的分布式锁
 | 
						|
type RedisLock struct {
 | 
						|
	key        string
 | 
						|
	value      string // 唯一标识,一般使用uuid
 | 
						|
	expiration time.Duration
 | 
						|
}
 | 
						|
 | 
						|
func NewLock(key string, expiration time.Duration) *RedisLock {
 | 
						|
	if key == "" || cli == nil {
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
	return &RedisLock{
 | 
						|
		key:        key,
 | 
						|
		value:      stringx.Rand(32),
 | 
						|
		expiration: expiration,
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Lock 添加分布式锁,expiration过期时间,小于等于0,不过期,需要通过 UnLock方法释放锁
 | 
						|
func (rl *RedisLock) Lock() bool {
 | 
						|
	result, err := cli.SetNX(context.Background(), LockKeyPrefix+rl.key, rl.value, rl.expiration).Result()
 | 
						|
	if err != nil {
 | 
						|
		global.Log.Errorf("redis lock setNx fail: %s", err.Error())
 | 
						|
		return false
 | 
						|
	}
 | 
						|
 | 
						|
	return result
 | 
						|
}
 | 
						|
 | 
						|
// TryLock 加锁重试五次
 | 
						|
func (rl *RedisLock) TryLock() bool {
 | 
						|
	var locked bool
 | 
						|
	for index := 0; index < 5; index++ {
 | 
						|
		locked = rl.Lock()
 | 
						|
		if locked {
 | 
						|
			return locked
 | 
						|
		}
 | 
						|
		time.Sleep(50 * time.Millisecond)
 | 
						|
	}
 | 
						|
 | 
						|
	return locked
 | 
						|
}
 | 
						|
 | 
						|
func (rl *RedisLock) UnLock() bool {
 | 
						|
	script := redis.NewScript(`
 | 
						|
	if redis.call("get", KEYS[1]) == ARGV[1] then
 | 
						|
		return redis.call("del", KEYS[1])
 | 
						|
	else
 | 
						|
		return 0
 | 
						|
	end
 | 
						|
	`)
 | 
						|
 | 
						|
	result, err := script.Run(context.Background(), cli, []string{LockKeyPrefix + rl.key}, rl.value).Int64()
 | 
						|
	if err != nil {
 | 
						|
		global.Log.Errorf("redis unlock runScript fail: %s", err.Error())
 | 
						|
		return false
 | 
						|
	}
 | 
						|
 | 
						|
	return result > 0
 | 
						|
}
 | 
						|
 | 
						|
// RefreshLock 存在则更新过期时间,不存在则创建key
 | 
						|
func (rl *RedisLock) RefreshLock() bool {
 | 
						|
	script := redis.NewScript(`
 | 
						|
	local val = redis.call("GET", KEYS[1])
 | 
						|
	if not val then
 | 
						|
		redis.call("setex", KEYS[1], ARGV[2], ARGV[1])
 | 
						|
		return 2
 | 
						|
	elseif val == ARGV[1] then
 | 
						|
		return redis.call("expire", KEYS[1], ARGV[2])
 | 
						|
	else
 | 
						|
		return 0
 | 
						|
	end
 | 
						|
	`)
 | 
						|
 | 
						|
	result, err := script.Run(context.Background(), cli, []string{LockKeyPrefix + rl.key}, rl.value, rl.expiration/time.Second).Int64()
 | 
						|
	if err != nil {
 | 
						|
		global.Log.Errorf("redis refreshLock runScript fail: %s", err.Error())
 | 
						|
		return false
 | 
						|
	}
 | 
						|
 | 
						|
	return result > 0
 | 
						|
}
 |