-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdlock_by_redis.go
92 lines (80 loc) · 2 KB
/
dlock_by_redis.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
package dlock
import (
"crypto/rc4"
"encoding/hex"
"math/rand"
"time"
log "github.com/sirupsen/logrus"
)
const (
_DlockRedisKey = "dlock"
// -1: failed to get; 0: failed to del; 1: success to del
_CheckAndDel = `if redis.call('get', KEYS[1]) == ARGV[1] then
return redis.call('del', KEYS[1])
else
return -1
end`
)
// DlockByRedis 通过redis实现的分布式锁服务
type DlockByRedis struct {
rdb RedisConnInterface
cipher *rc4.Cipher
}
// NewDlockByRedis 获取DlockByRedis实例.
func NewDlockByRedis(rdb RedisConnInterface) *DlockByRedis {
inst := &DlockByRedis{
rdb: rdb,
}
key := make([]byte, 32)
rand.Read(key)
inst.cipher, _ = rc4.NewCipher(key)
return inst
}
// TryLock 尝试获取分布式锁, 超时后就放弃 (不可重入锁).
func (dlr *DlockByRedis) TryLock(pid string, expire /* in milliseconds */, timeout int64 /* in seconds */) (token string, acquired bool) {
if expire <= 0 {
expire = 30000
}
if timeout <= 0 {
timeout = 1
}
rv := dlr.random()
timeout = time.Now().Unix() + timeout
LOOP:
for {
v, err := dlr.rdb.ExecCmd("SET", _DlockRedisKey, rv, "NX", "PX", expire)
if err != nil {
log.WithField("pid", pid).WithError(err).Error("failed to acquire lock")
acquired = false
break LOOP
}
if v != nil && v.(string) == "OK" {
token = rv
acquired = true
break LOOP
}
if time.Now().Unix() > timeout {
log.WithField("pid", pid).Warn("timeout to acquire lock")
acquired = false
break LOOP
}
}
return
}
// Unlock 释放分布式锁.
func (dlr *DlockByRedis) Unlock(pid, token string) {
v, err := dlr.rdb.ExecLuaScript(_CheckAndDel, 1, _DlockRedisKey, token)
if err != nil {
log.WithField("pid", pid).WithError(err).Error("failed to release lock")
}
if v == nil || v.(int64) != 1 {
log.WithField("pid", pid).WithError(err).Error("failed to release lock")
}
}
func (dlr *DlockByRedis) random() string {
src := make([]byte, 20)
rand.Read(src)
dst := make([]byte, 20)
dlr.cipher.XORKeyStream(dst, src)
return hex.EncodeToString(dst)
}