golang redis实现分布式锁

package main

import (
	"context"
	"fmt"
	"time"

	"github.com/go-redis/redis/v8"
)

func main() {
	redisCli := redis.NewClient(&redis.Options{
		Addr:     "127.0.0.1:6379",
		Password: "123456",
		DB:       0,
	})
	redisLock := NewRedisLock("test_key1", "test_value1", 1*time.Minute, redisCli)
	locked, err := redisLock.TryLock()
	fmt.Printf("locked:%v,err:%v\n", locked, err)
}

// RedisLock redis实现的分布式锁
type RedisLock struct {
	key        string
	value      string // 唯一标识,一般使用uuid
	expiration time.Duration
	redisCli   *redis.Client
}

func NewRedisLock(key, value string, expiration time.Duration, cli *redis.Client) *RedisLock {
	if key == "" || value == "" || cli == nil {
		return nil
	}
	return &RedisLock{
		key:        key,
		value:      value,
		expiration: expiration,
		redisCli:   cli,
	}
}

// Lock 添加分布式锁,expiration过期时间,小于等于0,不过期,需要通过 UnLock方法释放锁
func (rl *RedisLock) Lock() (bool, error) {
	result, err := rl.redisCli.SetNX(context.Background(), rl.key, rl.value, rl.expiration).Result()
	if err != nil {
		return false, err
	}

	return result, nil
}

func (rl *RedisLock) TryLock(waitTime time.Duration) (bool, error) {
	var onceWaitTime = 20 * time.Millisecond
	if waitTime < onceWaitTime {
		waitTime = onceWaitTime
	}

	for index := 0; index < int(waitTime/onceWaitTime); index++ {
		locked, err := rl.Lock()
		if locked || err != nil {
			return locked, err
		}
		time.Sleep(onceWaitTime)
	}

	return false, nil
}

func (rl *RedisLock) UnLock() (bool, error) {
	script := redis.NewScript(`
	if redis.call("get", KEYS[1]) == ARGV[1] then
		return redis.call("del", KEYS[1])
	else
		return 0
	end
	`)

	result, err := script.Run(context.Background(), rl.redisCli, []string{rl.key}, rl.value).Int64()
	if err != nil {
		return false, err
	}

	return result > 0, nil
}

// RefreshLock 存在则更新过期时间,不存在则创建key
func (rl *RedisLock) RefreshLock() (bool, error) {
	script := redis.NewScript(`
	local val = redis.call("GET", KEYS[1])
	if not val then
		redis.call("setex", KEYS[1], ARGV[2], ARGV[1])
		return 2
	elseif val == ARGV[1] then
		return redis.call("expire", KEYS[1], ARGV[2])
	else
		return 0
	end
	`)

	result, err := script.Run(context.Background(), rl.redisCli, []string{rl.key}, rl.value, rl.expiration/time.Second).Int64()
	if err != nil {
		return false, err
	}

	return result > 0, nil
}

实现 Redis 分布式锁的基本思路是利用 Redis 的 SETNX 命令(SET if Not eXists)实现。SETNX 命令会在 key 不存在的情况下,将 key 的值设为 value,如果 key 已经存在,则不做任何操作。 以下是一个简单的 Golang 实现 Redis 分布式锁的代码示例: ```go package redislock import ( "fmt" "time" "github.com/go-redis/redis/v7" ) type RedisLock struct { redisClient *redis.Client key string value string expiration time.Duration } func NewRedisLock(redisClient *redis.Client, key, value string, expiration time.Duration) *RedisLock { return &RedisLock{ redisClient: redisClient, key: key, value: value, expiration: expiration, } } func (r *RedisLock) Lock() (bool, error) { success, err := r.redisClient.SetNX(r.key, r.value, r.expiration).Result() if err != nil { return false, err } return success, nil } func (r *RedisLock) Unlock() error { err := r.redisClient.Del(r.key).Err() if err != nil { return err } return nil } ``` 在上面的代码中,NewRedisLock 函数用于创建一个 RedisLock 实例,需要传入 Redis 客户端、锁的 key、锁的值、锁的过期时间。Lock 方法用于尝试获取锁,如果获取成功,返回 true,否则返回 false。Unlock 方法用于释放锁。 以下是一个简单的使用示例: ```go package main import ( "fmt" "time" "github.com/go-redis/redis/v7" "github.com/yourusername/redislock" ) func main() { redisClient := redis.NewClient(&redis.Options{ Addr: "localhost:6379", DB: 0, }) lock := redislock.NewRedisLock(redisClient, "my-lock", "my-value", 10*time.Second) success, err := lock.Lock() if err != nil { fmt.Println("failed to acquire lock:", err) return } if !success { fmt.Println("lock is already held by another process") return } defer lock.Unlock() // Do some work } ``` 在上面的示例中,我们创建了一个 Redis 客户端,并且创建了一个 RedisLock 实例。然后,我们调用 Lock 方法尝试获取锁,如果获取成功,就可以进行一些需要加锁的操作。最后,我们调用 Unlock 方法释放锁。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值