Last updated
(base) yanglei@yuanhong distributeLock % tree ./
./
├── go.mod
├── go.sum
├── lock
│ └── lock.go
└── main.go
1 directory, 4 filespackage lock
import (
"github.com/gomodule/redigo/redis"
"math/rand"
)
type DistributeLock struct {
Key string
value int
TTL int
Conn redis.Conn
}
// Acquire 获取锁
func (l *DistributeLock) Acquire() bool {
result, err := redis.String(l.Conn.Do("SET", l.Key, l.value, "NX", "EX", l.TTL))
if err != nil {
return false
}
return result == "OK"
}
// Release 释放锁
func (l *DistributeLock) Release() bool {
// KEY存在 且 VALUE和设置时的值相同,才能删除
luaScript := `
if redis.call("GET", KEYS[1]) == ARGV[1] then
return redis.call("DEL", KEYS[1])
else
return 0
end
`
script := redis.NewScript(1, luaScript)
result, err := redis.Int(script.Do(l.Conn, l.Key, l.value))
if err != nil {
return false
}
return result == 1
}
// GenValue 生成随机值作为锁的value
func (l *DistributeLock) GenValue() {
l.value = rand.Int()
}package main
import (
"distributeLock/lock"
"fmt"
"github.com/gomodule/redigo/redis"
"log"
"sync"
"time"
)
func main() {
// 创建锁1
conn1, err := redis.Dial("tcp", "127.0.0.1:6379")
if err != nil {
log.Fatalf("conn error: %v\n", err)
}
distributeLock1 := lock.DistributeLock{
Key: "flag",
TTL: 60,
Conn: conn1,
}
distributeLock1.GenValue()
defer conn1.Close()
// 创建锁2
conn2, err := redis.Dial("tcp", "127.0.0.1:6379")
if err != nil {
log.Fatalf("conn error: %v\n", err)
}
distributeLock2 := lock.DistributeLock{
Key: "flag",
TTL: 60,
Conn: conn2,
}
distributeLock2.GenValue()
defer conn2.Close()
// 创建锁3
conn3, err := redis.Dial("tcp", "127.0.0.1:6379")
if err != nil {
log.Fatalf("conn error: %v\n", err)
}
distributeLock3 := lock.DistributeLock{
Key: "flag",
TTL: 60,
Conn: conn3,
}
distributeLock3.GenValue()
defer conn3.Close()
// 启动3个goroutine
wg := &sync.WaitGroup{}
wg.Add(3)
go retrieveLock(1, wg, distributeLock1)
go retrieveLock(2, wg, distributeLock2)
go retrieveLock(3, wg, distributeLock3)
wg.Wait()
}
func retrieveLock(i int, wg *sync.WaitGroup, distributeLock lock.DistributeLock) {
haveRetrieve := false
for !haveRetrieve {
// 获取锁
if distributeLock.Acquire() {
fmt.Printf("goroutine %d acquire lock success\n", i)
haveRetrieve = true
} else {
fmt.Printf("goroutine %d acquire lock failed, retry soon\n", i)
time.Sleep(2 * time.Second)
continue
}
// 以等待5s作为假定的业务处理时间
fmt.Printf("goroutine %d is processing\n", i)
time.Sleep(5 * time.Second)
// 释放锁
if distributeLock.Release() {
fmt.Printf("goroutine %d release lock success\n", i)
} else {
fmt.Printf("goroutine %d release lock failed\n", i)
}
}
wg.Done()
}(base) yanglei@yuanhong distributeLock % go run main.go
goroutine 1 acquire lock success
goroutine 1 is processing
goroutine 3 acquire lock failed, retry soon
goroutine 2 acquire lock failed, retry soon
goroutine 2 acquire lock failed, retry soon
goroutine 3 acquire lock failed, retry soon
goroutine 2 acquire lock failed, retry soon
goroutine 3 acquire lock failed, retry soon
goroutine 1 release lock success
goroutine 3 acquire lock success
goroutine 3 is processing
goroutine 2 acquire lock failed, retry soon
goroutine 2 acquire lock failed, retry soon
goroutine 2 acquire lock failed, retry soon
goroutine 3 release lock success
goroutine 2 acquire lock success
goroutine 2 is processing
goroutine 2 release lock success