go-redis配置
package config
import (
"context"
"fmt"
"github.com/redis/go-redis/v9"
)
var Ctx = context.Background()
var Rdb *redis.Client
func init() {
Rdb = redis.NewClient(&redis.Options{
Addr: "localhost:6379", // Redis 服务器地址
Password: "", // 没有密码则留空
DB: 0, // 使用默认数据库
})
// 测试连接
pong, err := Rdb.Ping(Ctx).Result()
if err != nil {
fmt.Println("无法连接到 Redis:", err)
return
}
fmt.Println("连接成功:", pong)
}
抢锁
func nxlock(uuidValue string, ttl int) {
var incrBy = redis.NewScript(`
--Lua 脚本
local key = KEYS[1] -- 获取第一个键参数
local value = ARGV[1] -- 获取第一个值参数
local ttl = ARGV[2] -- 获取第二个值参数
local result = redis.call("SETNX", key, value) -- 使用 redis.call 来执行 SETNX 命令
if result == 1 then
redis.call("EXPIRE", key, ttl)
end
return result -- 返回结果
`)
result, err := incrBy.Run(config.Ctx, config.Rdb, []string{"keyz"}, uuidValue, ttl).Int()
if err != nil {
fmt.Println("Error:", err)
} else {
if result == 0 {
fmt.Println("抢锁失败")
//超时配置
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(ttl)*time.Second)
defer cancel()
//订阅
pubsub := config.Rdb.Subscribe(ctx, "keyz")
defer pubsub.Close()
_, err := pubsub.ReceiveMessage(ctx) //阻塞订阅
if err != nil {
// 检查是否是超时错误
if err == context.DeadlineExceeded {
fmt.Println("接收消息超时")
//(自旋)
nxlock(uuidValue, ttl)
} else {
fmt.Printf("接收消息失败: %v\n", err)
}
return
}
//(自旋)
nxlock(uuidValue, ttl)
// fmt.Println(msg.Channel, msg.Payload)
}
if result == 1 {
fmt.Println("抢锁成功")
go watchdog(uuidValue, ttl)
}
}
}
释放锁
func unlock(uuidValue string) {
var incrBy = redis.NewScript(`
--Lua 脚本
local key = KEYS[1] -- 获取第一个键参数
local value = ARGV[1] -- 获取第一个值参数
local uuidvalue = redis.call("GET", key)
local result
if uuidvalue == value then
result = redis.call("DEL", key) -- 删除锁
else
result = 0
end
return result -- 返回结果
`)
result, err := incrBy.Run(config.Ctx, config.Rdb, []string{"keyz"}, uuidValue).Int()
if err != nil {
fmt.Println("Error:", err)
return
}
if result == 1 {
fmt.Println("删锁成功")
// 发布消息
err := config.Rdb.Publish(config.Ctx, "keyz", "unlock").Err()
if err != nil {
panic(err)
}
}
}
看门狗机制
func watchdog(uuidValue string, ttl int) {
for i := 0; i < 3; i++ {
time.Sleep(time.Duration(ttl/2) * time.Second)
var incrBy = redis.NewScript(`
--Lua 脚本
local key = KEYS[1] -- 获取第一个键参数
local value = ARGV[1] -- 获取第一个值参数
local ttl = ARGV[2] -- 获取第二个值参数
local uuidvalue = redis.call("GET", key)
local result
if uuidvalue == value then
result = redis.call("EXPIRE", key, ttl)
else
result = 0
end
return result -- 返回结果
`)
result, err := incrBy.Run(config.Ctx, config.Rdb, []string{"keyz"}, uuidValue, ttl).Int()
if err != nil {
fmt.Println("Error:", err)
return
}
if result == 0 {
break
} else {
fmt.Println("看门狗续期成功:", result)
}
}
}
gitee代码
https://gitee.com/alsark/redis-distributed-lock.githttps://gitee.com/alsark/redis-distributed-lock.git
https://gitee.com/alsark/redis-distributed-lock.git