-
Notifications
You must be signed in to change notification settings - Fork 7
/
locker.go
92 lines (74 loc) · 1.8 KB
/
locker.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 redis
import (
"context"
"errors"
"github.com/gone-io/gone"
"github.com/google/uuid"
"time"
)
const unlockLua = `if redis.call("get",KEYS[1]) == ARGV[1] then
return redis.call("del",KEYS[1])
else
return 0
end`
var ErrorLockFailed = errors.New("not lock success")
func NewRedisLocker() (gone.Goner, gone.GonerId, gone.GonerOption) {
return &locker{}, gone.IdGoneRedisLocker, gone.IsDefault(new(Locker))
}
type locker struct {
tracer gone.Tracer `gone:"gone-tracer"`
*inner `gone:"gone-redis-inner"`
k Key `gone:"gone-redis-key"`
}
type Unlock func()
func (r *locker) TryLock(key string, expiresIn time.Duration) (unlock Unlock, err error) {
conn := r.getConn()
defer r.close(conn)
key = r.buildKey(key)
v := uuid.NewString()
reply, err := conn.Do("SET", key, v, "NX", "PX", expiresIn.Milliseconds())
if err != nil {
return nil, err
}
if reply != "OK" {
r.Warnf("reply:%v", reply)
return nil, ErrorLockFailed
}
return func() {
r.releaseLock(key, v)
}, nil
}
func (r *locker) releaseLock(key, value string) {
conn := r.getConn()
defer r.close(conn)
_, err := conn.Do("EVAL", unlockLua, 1, key, value)
if err != nil {
r.Errorf("release lock error for key=%s", key)
}
}
func (r *locker) LockAndDo(key string, fn func(), lockTime, checkPeriod time.Duration) (err error) {
unlock, err := r.TryLock(key, lockTime)
if err != nil {
return err
}
defer unlock()
cancelCtx, stopWatch := context.WithCancel(context.Background())
defer stopWatch()
//监听任务完成,给锁续期
r.tracer.Go(func() {
for {
select {
case <-cancelCtx.Done():
r.Debugf("lock watch end")
return
case <-time.After(checkPeriod):
err := r.k.Expire(key, lockTime)
if err != nil {
r.Errorf("对 key=%s 续期失败", key)
}
}
}
})
fn()
return nil
}