美文网首页
go版本redis分布式锁

go版本redis分布式锁

作者: 耗子_aca3 | 来源:发表于2023-05-07 00:09 被阅读0次
package redis_client

import (
    "context"
    "fmt"
    "github.com/go-redis/redis/v8"
    "github.com/google/uuid"
    "time"
)

type RedisLock struct {
    redisClient   *redis.Client
    wait          bool // 是否等待
    lockKey       string
    lockValue     string
    timeout       time.Duration
    waitTimeout   time.Duration
    retryInterval time.Duration
}

func NewRedisLock(redisClient *redis.Client, lockKey string) *RedisLock {
    return &RedisLock{
        redisClient:   redisClient,
        wait:          true,
        lockKey:       fmt.Sprintf("s:lock:%s", lockKey),
        timeout:       time.Second * 5,
        waitTimeout:   time.Second * 5,
        retryInterval: time.Millisecond * 100,
    }
}

func (rl *RedisLock) SetWait(wait bool) *RedisLock {
    rl.wait = wait
    return rl
}

func (rl *RedisLock) SetTimeout(timeout time.Duration) *RedisLock {
    rl.timeout = timeout
    return rl
}

func (rl *RedisLock) SetWaitTimeout(waitTimeout time.Duration) *RedisLock {
    rl.waitTimeout = waitTimeout
    return rl
}

func (rl *RedisLock) SetRetryInterval(retryInterval time.Duration) *RedisLock {
    rl.retryInterval = retryInterval
    return rl
}

func (rl *RedisLock) Lock() (bool, error) {
    // 生成随机 UUID 作为锁的值
    rl.lockValue = uuid.New().String()
    ctx, cancel := context.WithTimeout(context.Background(), rl.waitTimeout)
    defer cancel()

    // 循环尝试获取锁
    for {
        select {
        case <-ctx.Done():
            return false, nil
        default:
            success, err := rl.redisClient.SetNX(ctx, rl.lockKey, rl.lockValue, rl.timeout).Result()
            if err != nil || (!success && !rl.wait) {
                return false, err
            }
            if success {
                return true, nil
            }
            time.Sleep(rl.retryInterval)
        }
    }
}

func (rl *RedisLock) Unlock() (bool, error) {
    script := "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"
    ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
    defer cancel()
    result, err := rl.redisClient.Eval(ctx, script, []string{rl.lockKey}, rl.lockValue).Int()
    if err != nil {
        return false, err
    }
    return result > 0, nil
}

相关文章

网友评论

      本文标题:go版本redis分布式锁

      本文链接:https://www.haomeiwen.com/subject/cxrhsdtx.html