90 lines
2.2 KiB
Go
90 lines
2.2 KiB
Go
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
|
|
}
|
|
}
|
|
}
|
|
}
|