go redis分布式锁带续命

10 篇文章 0 订阅
package main

import (
    "context"
    "fmt"
    "github.com/go-redis/redis"
    uuid "github.com/satori/go.uuid"
    "strconv"
    "time"
)

type RedisLock struct {
    //保存连接
    rdb            *redis.Client
    err            error
    lockValue      string
    lockName       string        // 锁名称
    ex             time.Duration // 锁过期时间
    perKeepAlive   time.Duration // 续命时间
    startKeepAlive time.Duration // 开始续命(小于过期时间)
    workerTime     int           // 模拟任务时间(秒)
}

var redisClient RedisLock

func init() {
    redisClient.rdb = redis.NewClient(&redis.Options{
        Addr:     "127.0.0.1:6379", // 指定
        Password: "123456",
        DB:       0,
    })
    _, redisClient.err = redisClient.rdb.Ping().Result()
    if redisClient.err != nil {
        panic(redisClient.err)
    }
    redisClient.lockName = "redisLock"
    redisClient.lockValue = uuid.NewV4().String()
    redisClient.ex = 4
    redisClient.startKeepAlive = 3
    redisClient.perKeepAlive = 4
    redisClient.workerTime = 5
}

func main() {
    worker()
}

func worker() {
    Lock(redisClient.lockName, redisClient.lockValue)
    ctx, cancel := context.WithTimeout(context.Background(), redisClient.startKeepAlive*time.Second)
    valueTX := context.WithValue(ctx, "KeepAlive", redisClient.perKeepAlive)
    go KeepAliveWorker(valueTX)
    test()
    cancel()
    defer UnLock(redisClient.lockName, redisClient.lockValue)

}

//模拟任务超时
func test() {
    for i := 1; i <= redisClient.workerTime; i++ {
        time.Sleep(time.Second)
        fmt.Println(i)
    }
}

// 加锁
func Lock(key, lockValue string) (bool, error) {
    bool, err := redisClient.rdb.SetNX(key, lockValue, redisClient.ex*time.Second).Result()
    if err != nil {
        return false, err
    }
    fmt.Println("上锁成功", "key:", key, "lockValue:", lockValue)
    return bool, nil
}

// 解锁
func UnLock(key, s string) bool {
    //解锁的脚本
    unLockScript := `local lockValue =  redis.call("GET", KEYS[1])
                    if lockValue == ARGV[1] then
                        -- 执行成功返回“1”
                        return redis.call("DEL", KEYS[1])
                    else
                        return 0
                    end`
    resp, err := redisClient.rdb.Eval(unLockScript, []string{key}, []string{s}).Result()

    //fmt.Printf("%T", resp)
    fmt.Println("解锁:", resp.(int64) == 1)
    if err != nil {
        fmt.Println("解锁失败", resp, err)
        return false
    }
    fmt.Println("解锁成功", resp)
    return true
}

//续租任务 通过超时和主动取消控制
func KeepAliveWorker(ctx context.Context) {
    //维持锁的脚本
    KeepAliveScript := `if ( redis.call("GET", KEYS[1]) )
                            then         
                                redis.call('expire', KEYS[1], ARGV[1])
                                return "ok"
                            end
                                return "fail"`

    for {
        select {
        case <-ctx.Done():
            // 执行续租任务
            res := redisClient.rdb.Eval(
                KeepAliveScript,
                []string{redisClient.lockName},
                []string{strconv.Itoa(int(redisClient.perKeepAlive))})
            if res.Err() != nil {
                panic(res.Err().Error())
            }

            if res.Val().(string) == "ok" {
                fmt.Println("key:", redisClient.lockName, "续命成功:", redisClient.perKeepAlive)
                return
            }
            fmt.Println("续命失败", res.Val())
            return
        }
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值