代码

获得锁 释放锁

分布式锁可用必须满足一下四个条件

  • 互斥性 在任意时刻 只有一个客户端能获取到锁
  • 不会发生死锁 即使有一个客户端 在持有锁的期间奔溃而没有主动解锁, 也能保证后续客户端能解锁
  • 具有容错性 只要大部分的redis 节点正常运行, 客户端就可以加锁和解锁
  • 解锁加锁同一个客户端。 加锁和解锁必须是同一个客户端,客户端不能把别人加的锁给解除了。

package main

import (
	"fmt"
	"github.com/gomodule/redigo/redis"
	"time"
)

// https://www.w3cschool.cn/redis/redis-yj3f2p0c.html
// redis 分布式锁实现
func init() {
	initRedis()
}

var redisPool *redis.Pool

func initRedis() {
	redisPool = &redis.Pool{
		MaxIdle:     10,
		MaxActive:   10,
		IdleTimeout: 5 * time.Second,
		Dial: func() (conn redis.Conn, e error) {
			conn, err := redis.Dial("tcp", "127.0.1.1:6379")
			if err != nil {
				return nil, err
			}
			conn.Do("select", 0)
			return conn, err
		},
	}
}

const LockName = "LockName"
const StringSetIfNotExist = "NX"
const StringSetWithExpireTime = "EX"

func getLock(id string) bool {
	// 获得锁
	// 一个命令原子操作  为了防止执行了nx 之后程序突然奔溃 则就会无法设置过期时间发生死锁
	conn := redisPool.Get()
	defer conn.Close()
	_, err := redis.String(conn.Do("set", LockName, id, StringSetIfNotExist, StringSetWithExpireTime, 10))
	if err == nil {
		return true
	}
	return false

}

const luaScript = `
if redis.call('get', KEYS[1])==ARGV[1] then
	return redis.call('del', KEYS[1])
else
	return 0
end
`

func releaseDistributedLock(id string) bool {
	// 释放分布式锁
	// 也必须是原子操作 借助于lua 脚本实现
	// 谁 上锁谁解锁
	conn := redisPool.Get()
	defer conn.Close()
	lua := redis.NewScript(1, luaScript) // 定义参数的个数 
	_, err := redis.Int(lua.Do(conn, LockName, id)) // 上面定义几个参数conn 后面几个都是参数, 参数的后面就是值 按照顺序
	if err == nil {
		return true
	}
	return false
}
func main() {
	theId := "yang"
	flag := getLock(theId)
	fmt.Println(flag)
	releaseFlag := releaseDistributedLock(theId)
	fmt.Println("releaseFlag: ", releaseFlag)
}