package redisLock import ( "context" "log" "time" "github.com/go-redis/redis/v8" ) type redisCommInfo struct { key string value string ttl int } type RedisLock struct { client *redis.Client ctx context.Context lockKeepAliveCh chan struct{} kvt redisCommInfo } func CreateRedisLock(c *redis.Client, ctx context.Context, k, v string, ttl int) *RedisLock { return &RedisLock{c, ctx, make(chan struct{}, 1), redisCommInfo{k, v, ttl}} } func (r *RedisLock) Lock() { //for r.client.SetNX(r.ctx, r.kvt.key, r.kvt.value, time.Duration(r.kvt.ttl)*time.Second).Val() == false { // time.Sleep(time.Millisecond * 20) //} script := `if redis.call('exists',KEYS[1]) == 0 or redis.call('hexists',KEYS[1],ARGV[1]) == 1 then redis.call('hincrby',KEYS[1],ARGV[1],1) redis.call('expire',KEYS[1],ARGV[2]) return 1 else return 0 end` for r.client.Eval(r.ctx, script, []string{r.kvt.key}, r.kvt.value, r.kvt.ttl).Val().(int64) == 0 { time.Sleep(time.Millisecond * 20) } select { case r.lockKeepAliveCh <- struct{}{}: go r.autoReNewExpire() default: } log.Printf("lock ok: %s,%s,%d\n", r.kvt.key, r.kvt.value, r.kvt.ttl) } func (r *RedisLock) Unlock() { //if r.client.Get(r.ctx, r.kvt.key).Val() == r.kvt.value { // r.client.Del(r.ctx, r.kvt.key) //} //script := "if (redis.call('get',KEYS[1]) == ARGV[1]) then return redis.call('del',KEYS[1]) else return 0 end" //r.client.Eval(r.ctx, script, []string{r.kvt.key}, r.kvt.value) script := `if redis.call('hexists',KEYS[1],ARGV[1]) == 0 then return nil elseif redis.call('hincrby',KEYS[1],ARGV[1],-1) == 0 then return redis.call('del',KEYS[1]) else return 0 end` r.client.Eval(r.ctx, script, []string{r.kvt.key}, r.kvt.value) log.Printf("unlock ok: %s,%s,%d\n", r.kvt.key, r.kvt.value, r.kvt.ttl) } func (r *RedisLock) autoReNewExpire() { defer func() { <-r.lockKeepAliveCh }() tc := time.NewTicker(time.Duration(r.kvt.ttl) * time.Second / 3) defer tc.Stop() for { select { case <-tc.C: script := `if redis.call('hexists',KEYS[1],ARGV[1]) == 1 then return redis.call('expire',KEYS[1],ARGV[2]) else return 0 end` if r.client.Eval(r.ctx, script, []string{r.kvt.key}, r.kvt.value).Val().(int64) == 0 { return } } } }