go分布式锁(redis,lua脚本,发布订阅)

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.githttps://gitee.com/alsark/redis-distributed-lock.git

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值