完整代码传送门
package redis_distributed_lock
import (
"context"
"fmt"
"redis-distributed-lock/redis_client"
"runtime"
"strings"
"time"
"github.com/redis/go-redis/v9"
)
type RedisDistributedLock struct {
client *redis.Client
lockKey string
lockValue string
expireTime int
}
const redisLockKey = "redisLock"
const redisLockExpireTime = 30
func NewRedisDistributedLock(client *redis.Client) *RedisDistributedLock {
return &RedisDistributedLock{
client: client,
lockKey: redisLockKey,
lockValue: GoID(),
expireTime: redisLockExpireTime,
}
}
func (r *RedisDistributedLock) Lock() {
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`
client := redis_client.NewClient()
for {
val, err := client.Eval(context.Background(), script, []string{r.lockKey}, r.lockValue, r.expireTime).Result()
if err != nil {
fmt.Printf("lock failed, err: %v\n", err)
return
}
if val != int64(1) {
time.Sleep(30 * time.Millisecond)
} else {
fmt.Printf("lock successfully, lockName: %v, lockValue: %v\n", r.lockKey, r.lockValue)
go r.renewExpire()
break
}
}
}
func (r *RedisDistributedLock) renewExpire() {
script := `
if redis.call("hexists", KEYS[1], ARGV[1]) == 1 then
return redis.call("expire", KEYS[1], ARGV[2])
else
return 0
end`
for {
time.Sleep(time.Duration(r.expireTime/3) * time.Second)
val, err := r.client.Eval(context.Background(), script, []string{r.lockKey}, r.lockValue, r.expireTime).Result()
if err != nil {
fmt.Printf("renewExpire failed, err: %v\n", err)
return
}
if val == int64(0) {
return
}
}
}
func (r *RedisDistributedLock) Unlock() {
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`
client := redis_client.NewClient()
val, err := client.Eval(context.Background(), script, []string{r.lockKey}, r.lockValue).Result()
if err != nil {
fmt.Printf("unlock failed, err: %v\n", err)
return
}
if val == redis.Nil {
fmt.Printf("lock does not exist\n")
return
}
fmt.Printf("unlock successfully, lockName: %v, lockValue: %v\n", r.lockKey, r.lockValue)
}
func GoID() string {
var buf [64]byte
n := runtime.Stack(buf[:], false)
return strings.Fields(strings.TrimPrefix(string(buf[:n]), "goroutine "))[0]
}