init read lock script of distributedlock
This commit is contained in:
parent
1899546ba4
commit
2c2c2811a7
|
|
@ -1,2 +1,3 @@
|
|||
# ModelRT
|
||||
|
||||
[](http://192.168.46.100:4080/CL-Softwares/modelRT)
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
// Package luascript defines the lua script used for redis distributed lock
|
||||
package luascript
|
||||
|
||||
// RlockScript is the lua script for the lock read lock command
|
||||
/*
|
||||
KEYS[1]:锁的键名(key),通常是锁的唯一标识。
|
||||
KEYS[2]:锁的超时键名前缀(rwTimeoutPrefix),用于存储每个读锁的超时键。
|
||||
ARGV[1]:锁的过期时间(lockLeaseTime),单位为毫秒。
|
||||
ARGV[2]:当前客户端的唯一标识(token),用于区分不同的客户端。
|
||||
*/
|
||||
var RlockScript = `local mode = redis.call('hget', KEYS[1], 'mode');
|
||||
local lockKey = KEYS[2] .. ARGV[2];
|
||||
if (mode == false) then
|
||||
redis.call('hset', KEYS[1], 'mode', 'read');
|
||||
redis.call('hset', KEYS[1], lockKey, '1');
|
||||
redis.call('hexpire', KEYS[1], ARGV[1] 'fields' '1' lockKey);
|
||||
redis.call('expire', KEYS[1], ARGV[1]);
|
||||
return 1;
|
||||
end;
|
||||
|
||||
if (mode == 'write') then
|
||||
return -1;
|
||||
end;
|
||||
|
||||
if (mode == 'read') then
|
||||
if (redis.call('exists', KEYS[1], ARGV[2]) == 1) then
|
||||
redis.call('hincrby', KEYS[1], lockKey, '1');
|
||||
local remainTime = redis.call('httl', KEYS[1], 'fields', '1', lockKey);
|
||||
redis.call('hexpire', key, math.max(remainTime, ARGV[1]));
|
||||
else
|
||||
redis.call('hset', KEYS[1], lockKey, '1');
|
||||
redis.call('hexpire', KEYS[1], lockKey, ARGV[1]);
|
||||
end;
|
||||
local cursor = 0;
|
||||
local maxRemainTime = tonumber(ARGV[1]);
|
||||
local pattern = KEYS[2] .. ':*';
|
||||
repeat
|
||||
local hscanResult = redis.call('hscan', KEYS[1], cursor, 'match', pattern, 'count', '100');
|
||||
cursor = tonumber(hscanResult[1]);
|
||||
local fields = hscanResult[2];
|
||||
|
||||
for i = 1, #fields,2 do
|
||||
local field = fields[i];
|
||||
local remainTime = redis.call('httl', KEYS[1], 'fields', '1', field);
|
||||
maxRemainTime = math.max(tonumber(remainTime[1]), maxRemainTime);
|
||||
end;
|
||||
until cursor == 0;
|
||||
|
||||
local remainTime = redis.call('ttl', KEYS[1]);
|
||||
redis.call('expire', KEYS[1], math.max(tonumber(remainTime),maxRemainTime));
|
||||
return 1;
|
||||
end;
|
||||
`
|
||||
|
||||
// TODO 优化读锁解锁语句
|
||||
// UnRlockScript is the lua script for the unlock read lock command
|
||||
/*
|
||||
KEYS[1]:锁的键名(key),通常是锁的唯一标识。
|
||||
KEYS[2]:锁的释放通知频道(chankey),用于通知其他客户端锁已释放。
|
||||
KEYS[3]:锁的超时键名前缀(rwTimeoutTokenPrefix),用于存储每个读锁的超时键。
|
||||
KEYS[4]:锁的超时键名前缀(prefixKey),用于存储每个读锁的超时键。
|
||||
ARGV[1]:解锁消息(unlockMessage),用于通知其他客户端锁已释放。
|
||||
ARGV[2]:当前客户端的唯一标识(token),用于区分不同的客户端。
|
||||
*/
|
||||
var UnRlockScript = `local mode = redis.call('hget', KEYS[1], 'mode');
|
||||
if (mode == false) then
|
||||
redis.call('publish', KEYS[2], ARGV[1]);
|
||||
return 1;
|
||||
end;
|
||||
local lockExists = redis.call('hexists', KEYS[1], ARGV[2]);
|
||||
if (lockExists == 0) then
|
||||
return nil;
|
||||
end;
|
||||
|
||||
local counter = redis.call('hincrby', KEYS[1], ARGV[2], -1);
|
||||
if (counter == 0) then
|
||||
redis.call('hdel', KEYS[1], ARGV[2]);
|
||||
end;
|
||||
redis.call('del', KEYS[3] .. ':' .. (counter+1));
|
||||
|
||||
if (redis.call('hlen', KEYS[1]) > 1) then
|
||||
local maxRemainTime = -3;
|
||||
local keys = redis.call('hkeys', KEYS[1]);
|
||||
for n, key in ipairs(keys) do
|
||||
counter = tonumber(redis.call('hget', KEYS[1], key));
|
||||
if type(counter) == 'number' then
|
||||
for i=counter, 1, -1 do
|
||||
local remainTime = redis.call('ttl', KEYS[4] .. ':' .. key .. ':rwlock_timeout:' .. i);
|
||||
maxRemainTime = math.max(remainTime, maxRemainTime);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
if maxRemainTime > 0 then
|
||||
redis.call('pexpire', KEYS[1], maxRemainTime);
|
||||
return 0;
|
||||
end;
|
||||
|
||||
if mode == 'write' then
|
||||
return 0;
|
||||
end;
|
||||
end;
|
||||
|
||||
redis.call('del', KEYS[1]);
|
||||
redis.call('publish', KEYS[2], ARGV[1]);
|
||||
return 1;
|
||||
`
|
||||
|
|
@ -0,0 +1,299 @@
|
|||
package distributed_lock
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
luascript "modelRT/distributedlock/luascript"
|
||||
|
||||
"github.com/go-redis/redis"
|
||||
uuid "github.com/google/uuid"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
var lockScript string = strings.Join([]string{
|
||||
"if (redis.call('exists', KEYS[1]) == 0) then ",
|
||||
"redis.call('hset', KEYS[1], ARGV[2], 1); ",
|
||||
"redis.call('pexpire', KEYS[1], ARGV[1]); ",
|
||||
"return nil; ",
|
||||
"end; ",
|
||||
"if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then ",
|
||||
"redis.call('hincrby', KEYS[1], ARGV[2], 1); ",
|
||||
"redis.call('pexpire', KEYS[1], ARGV[1]); ",
|
||||
"return nil; ",
|
||||
"end; ",
|
||||
"return redis.call('pttl', KEYS[1]);",
|
||||
}, "")
|
||||
|
||||
var refreshLockScript string = strings.Join([]string{
|
||||
"if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then ",
|
||||
"redis.call('pexpire', KEYS[1], ARGV[1]); ",
|
||||
"return 1; ",
|
||||
"end; ",
|
||||
"return 0;",
|
||||
}, "")
|
||||
|
||||
var unlockScript string = strings.Join([]string{
|
||||
"if (redis.call('exists', KEYS[1]) == 0) then ",
|
||||
"redis.call('publish', KEYS[2], ARGV[1]); ",
|
||||
"return 1; ",
|
||||
"end;",
|
||||
"if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then ",
|
||||
"return nil;",
|
||||
"end; ",
|
||||
"local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1); ",
|
||||
"if (counter > 0) then ",
|
||||
"redis.call('pexpire', KEYS[1], ARGV[2]); ",
|
||||
"return 0; ",
|
||||
"else ",
|
||||
"redis.call('del', KEYS[1]); ",
|
||||
"redis.call('publish', KEYS[2], ARGV[1]); ",
|
||||
"return 1; ",
|
||||
"end; ",
|
||||
"return nil;",
|
||||
}, "")
|
||||
|
||||
const (
|
||||
internalLockLeaseTime = uint64(30) * 1000
|
||||
unlockMessage = 0
|
||||
)
|
||||
|
||||
type RedissionLockConfig struct {
|
||||
LockLeaseTime time.Duration
|
||||
Prefix string
|
||||
ChanPrefix string
|
||||
Key string
|
||||
}
|
||||
|
||||
type redissionLocker struct {
|
||||
token string
|
||||
key string
|
||||
chankey string
|
||||
exit chan struct{}
|
||||
lockLeaseTime uint64
|
||||
client *redis.Client
|
||||
once *sync.Once
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
func (rl *redissionLocker) Lock(ctx context.Context, timeout ...time.Duration) {
|
||||
fmt.Println(luascript.RlockScript)
|
||||
if rl.exit == nil {
|
||||
rl.exit = make(chan struct{})
|
||||
}
|
||||
ttl, err := rl.tryLock()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if ttl <= 0 {
|
||||
rl.once.Do(func() {
|
||||
go rl.refreshLockTimeout()
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
submsg := make(chan struct{}, 1)
|
||||
defer close(submsg)
|
||||
sub := rl.client.Subscribe(rl.chankey)
|
||||
defer sub.Close()
|
||||
go rl.subscribeLock(sub, submsg)
|
||||
// listen := rl.listenManager.Subscribe(rl.key, rl.token)
|
||||
// defer rl.listenManager.UnSubscribe(rl.key, rl.token)
|
||||
|
||||
timer := time.NewTimer(ttl)
|
||||
defer timer.Stop()
|
||||
// outimer 的作用理解为如果超过多长时间无法获得这个锁,那么就直接放弃
|
||||
var outimer *time.Timer
|
||||
if len(timeout) > 0 && timeout[0] > 0 {
|
||||
outimer = time.NewTimer(timeout[0])
|
||||
}
|
||||
LOOP:
|
||||
for {
|
||||
ttl, err = rl.tryLock()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if ttl <= 0 {
|
||||
rl.once.Do(func() {
|
||||
go rl.refreshLockTimeout()
|
||||
})
|
||||
return
|
||||
}
|
||||
if outimer != nil {
|
||||
select {
|
||||
case _, ok := <-submsg:
|
||||
if !timer.Stop() {
|
||||
<-timer.C
|
||||
}
|
||||
|
||||
if !ok {
|
||||
panic("lock listen release")
|
||||
}
|
||||
|
||||
timer.Reset(ttl)
|
||||
case <-ctx.Done():
|
||||
// break LOOP
|
||||
panic("lock context already release")
|
||||
case <-timer.C:
|
||||
timer.Reset(ttl)
|
||||
case <-outimer.C:
|
||||
if !timer.Stop() {
|
||||
<-timer.C
|
||||
}
|
||||
break LOOP
|
||||
}
|
||||
} else {
|
||||
select {
|
||||
case _, ok := <-submsg:
|
||||
if !timer.Stop() {
|
||||
<-timer.C
|
||||
}
|
||||
|
||||
if !ok {
|
||||
panic("lock listen release")
|
||||
}
|
||||
|
||||
timer.Reset(ttl)
|
||||
case <-ctx.Done():
|
||||
// break LOOP
|
||||
panic("lock context already release")
|
||||
case <-timer.C:
|
||||
timer.Reset(ttl)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (rl *redissionLocker) subscribeLock(sub *redis.PubSub, out chan struct{}) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
rl.logger.Error("subscribeLock catch error", zap.Error(err.(error)))
|
||||
}
|
||||
}()
|
||||
if sub == nil || out == nil {
|
||||
return
|
||||
}
|
||||
rl.logger.Debug("lock:%s enter sub routine", zap.String("token", rl.token))
|
||||
LOOP:
|
||||
for {
|
||||
msg, err := sub.Receive()
|
||||
if err != nil {
|
||||
rl.logger.Info("sub receive message", zap.Error(err))
|
||||
break LOOP
|
||||
}
|
||||
|
||||
select {
|
||||
case <-rl.exit:
|
||||
break LOOP
|
||||
default:
|
||||
if len(out) > 0 {
|
||||
// if channel hava msg. drop it
|
||||
rl.logger.Debug("drop message when channel if full")
|
||||
continue
|
||||
}
|
||||
|
||||
switch msg.(type) {
|
||||
case *redis.Subscription:
|
||||
// Ignore.
|
||||
case *redis.Pong:
|
||||
// Ignore.
|
||||
case *redis.Message:
|
||||
out <- struct{}{}
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
rl.logger.Debug("lock sub routine release", zap.String("token", rl.token))
|
||||
}
|
||||
|
||||
func (rl *redissionLocker) refreshLockTimeout() {
|
||||
rl.logger.Debug("lock", zap.String("token", rl.token), zap.String("lock key", rl.key))
|
||||
lockTime := time.Duration(rl.lockLeaseTime/3) * time.Millisecond
|
||||
timer := time.NewTimer(lockTime)
|
||||
defer timer.Stop()
|
||||
LOOP:
|
||||
for {
|
||||
select {
|
||||
case <-timer.C:
|
||||
timer.Reset(lockTime)
|
||||
// update key expire time
|
||||
res := rl.client.Eval(refreshLockScript, []string{rl.key}, rl.lockLeaseTime, rl.token)
|
||||
val, err := res.Int()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if val == 0 {
|
||||
rl.logger.Debug("not find the lock key of self")
|
||||
break LOOP
|
||||
}
|
||||
case <-rl.exit:
|
||||
break LOOP
|
||||
|
||||
}
|
||||
}
|
||||
rl.logger.Debug("refresh routine release", zap.String("token", rl.token))
|
||||
}
|
||||
|
||||
func (rl *redissionLocker) cancelRefreshLockTime() {
|
||||
if rl.exit != nil {
|
||||
close(rl.exit)
|
||||
rl.exit = nil
|
||||
rl.once = &sync.Once{}
|
||||
}
|
||||
}
|
||||
|
||||
func (rl *redissionLocker) tryLock() (time.Duration, error) {
|
||||
res := rl.client.Eval(lockScript, []string{rl.key}, rl.lockLeaseTime, rl.token)
|
||||
v, err := res.Result()
|
||||
if err != redis.Nil && err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if v == nil {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
return time.Duration(v.(int64)), nil
|
||||
}
|
||||
|
||||
func (rl *redissionLocker) UnLock() {
|
||||
res := rl.client.Eval(unlockScript, []string{rl.key, rl.chankey}, unlockMessage, rl.lockLeaseTime, rl.token)
|
||||
val, err := res.Result()
|
||||
if err != redis.Nil && err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if val == nil {
|
||||
panic("attempt to unlock lock, not locked by current routine by lock id:" + rl.token)
|
||||
}
|
||||
rl.logger.Debug("unlock", zap.String("token", rl.token), zap.String("key", rl.key))
|
||||
if val.(int64) == 1 {
|
||||
rl.cancelRefreshLockTime()
|
||||
}
|
||||
}
|
||||
|
||||
func GetLocker(client *redis.Client, ops *RedissionLockConfig) *redissionLocker {
|
||||
r := &redissionLocker{
|
||||
token: uuid.New().String(),
|
||||
client: client,
|
||||
exit: make(chan struct{}),
|
||||
once: &sync.Once{},
|
||||
}
|
||||
|
||||
if len(ops.Prefix) <= 0 {
|
||||
ops.Prefix = "redission-lock"
|
||||
}
|
||||
if len(ops.ChanPrefix) <= 0 {
|
||||
ops.ChanPrefix = "redission-lock-channel"
|
||||
}
|
||||
if ops.LockLeaseTime == 0 {
|
||||
r.lockLeaseTime = internalLockLeaseTime
|
||||
}
|
||||
r.key = strings.Join([]string{ops.Prefix, ops.Key}, ":")
|
||||
r.chankey = strings.Join([]string{ops.ChanPrefix, ops.Key}, ":")
|
||||
return r
|
||||
}
|
||||
|
|
@ -0,0 +1,471 @@
|
|||
package distributed_lock
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/go-redis/redis"
|
||||
uuid "github.com/google/uuid"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
var rlockScript string = strings.Join([]string{
|
||||
"local mode = redis.call('hget', KEYS[1], 'mode'); ",
|
||||
"if (mode == false) then ",
|
||||
"redis.call('hset', KEYS[1], 'mode', 'read'); ",
|
||||
"redis.call('hset', KEYS[1], ARGV[2], 1); ",
|
||||
"redis.call('set', KEYS[2] .. ':1', 1); ",
|
||||
"redis.call('pexpire', KEYS[2] .. ':1', ARGV[1]); ",
|
||||
"redis.call('pexpire', KEYS[1], ARGV[1]); ",
|
||||
"return nil; ",
|
||||
"end; ",
|
||||
"if (mode == 'read') or (mode == 'write' and redis.call('hexists', KEYS[1], ARGV[3]) == 1) then ",
|
||||
"local ind = redis.call('hincrby', KEYS[1], ARGV[2], 1); ",
|
||||
"local key = KEYS[2] .. ':' .. ind;",
|
||||
"redis.call('set', key, 1); ",
|
||||
"redis.call('pexpire', key, ARGV[1]); ",
|
||||
"local remainTime = redis.call('pttl', KEYS[1]); ",
|
||||
"redis.call('pexpire', KEYS[1], math.max(remainTime, ARGV[1])); ",
|
||||
"return nil; ",
|
||||
"end;",
|
||||
"return redis.call('pttl', KEYS[1]);",
|
||||
}, "")
|
||||
|
||||
var runlockScript string = strings.Join([]string{
|
||||
"local mode = redis.call('hget', KEYS[1], 'mode'); ",
|
||||
"if (mode == false) then ",
|
||||
"redis.call('publish', KEYS[2], ARGV[1]); ",
|
||||
"return 1; ",
|
||||
"end; ",
|
||||
"local lockExists = redis.call('hexists', KEYS[1], ARGV[2]); ",
|
||||
"if (lockExists == 0) then ",
|
||||
"return nil;",
|
||||
"end; ",
|
||||
|
||||
"local counter = redis.call('hincrby', KEYS[1], ARGV[2], -1); ",
|
||||
"if (counter == 0) then ",
|
||||
"redis.call('hdel', KEYS[1], ARGV[2]); ",
|
||||
"end;",
|
||||
"redis.call('del', KEYS[3] .. ':' .. (counter+1)); ",
|
||||
|
||||
"if (redis.call('hlen', KEYS[1]) > 1) then ",
|
||||
"local maxRemainTime = -3; ",
|
||||
"local keys = redis.call('hkeys', KEYS[1]); ",
|
||||
"for n, key in ipairs(keys) do ",
|
||||
"counter = tonumber(redis.call('hget', KEYS[1], key)); ",
|
||||
"if type(counter) == 'number' then ",
|
||||
"for i=counter, 1, -1 do ",
|
||||
"local remainTime = redis.call('pttl', KEYS[4] .. ':' .. key .. ':rwlock_timeout:' .. i); ",
|
||||
"maxRemainTime = math.max(remainTime, maxRemainTime);",
|
||||
"end; ",
|
||||
"end; ",
|
||||
"end; ",
|
||||
|
||||
"if maxRemainTime > 0 then ",
|
||||
"redis.call('pexpire', KEYS[1], maxRemainTime); ",
|
||||
"return 0; ",
|
||||
"end;",
|
||||
|
||||
"if mode == 'write' then ",
|
||||
"return 0;",
|
||||
"end; ",
|
||||
"end; ",
|
||||
|
||||
"redis.call('del', KEYS[1]); ",
|
||||
"redis.call('publish', KEYS[2], ARGV[1]); ",
|
||||
"return 1; ",
|
||||
}, "")
|
||||
|
||||
var rlockrefreshScript = strings.Join([]string{
|
||||
"local counter = redis.call('hget', KEYS[1], ARGV[2]); ",
|
||||
"if (counter ~= false) then ",
|
||||
"redis.call('pexpire', KEYS[1], ARGV[1]); ",
|
||||
|
||||
"if (redis.call('hlen', KEYS[1]) > 1) then ",
|
||||
"local keys = redis.call('hkeys', KEYS[1]); ",
|
||||
"for n, key in ipairs(keys) do ",
|
||||
"counter = tonumber(redis.call('hget', KEYS[1], key)); ",
|
||||
"if type(counter) == 'number' then ",
|
||||
"for i=counter, 1, -1 do ",
|
||||
"redis.call('pexpire', KEYS[2] .. ':' .. key .. ':rwlock_timeout:' .. i, ARGV[1]); ",
|
||||
"end; ",
|
||||
"end; ",
|
||||
"end; ",
|
||||
"end; ",
|
||||
|
||||
"return 1; ",
|
||||
"end; ",
|
||||
"return 0;",
|
||||
}, "")
|
||||
|
||||
var wlockScript string = strings.Join([]string{
|
||||
"local mode = redis.call('hget', KEYS[1], 'mode'); ",
|
||||
"if (mode == false) then ",
|
||||
"redis.call('hset', KEYS[1], 'mode', 'write'); ",
|
||||
"redis.call('hset', KEYS[1], ARGV[2], 1); ",
|
||||
"redis.call('pexpire', KEYS[1], ARGV[1]); ",
|
||||
"return nil; ",
|
||||
"end; ",
|
||||
"if (mode == 'write') then ",
|
||||
"if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then ",
|
||||
"redis.call('hincrby', KEYS[1], ARGV[2], 1); ",
|
||||
"local currentExpire = redis.call('pttl', KEYS[1]); ",
|
||||
"redis.call('pexpire', KEYS[1], currentExpire + ARGV[1]); ",
|
||||
"return nil; ",
|
||||
"end; ",
|
||||
"end;",
|
||||
"return redis.call('pttl', KEYS[1]);",
|
||||
}, "")
|
||||
|
||||
var wunlockScript string = strings.Join([]string{
|
||||
"local mode = redis.call('hget', KEYS[1], 'mode'); ",
|
||||
"if (mode == false) then ",
|
||||
"redis.call('publish', KEYS[2], ARGV[1]); ",
|
||||
"return 1; ",
|
||||
"end;",
|
||||
"if (mode == 'write') then ",
|
||||
"local lockExists = redis.call('hexists', KEYS[1], ARGV[3]); ",
|
||||
"if (lockExists == 0) then ",
|
||||
"return nil;",
|
||||
"else ",
|
||||
"local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1); ",
|
||||
"if (counter > 0) then ",
|
||||
"redis.call('pexpire', KEYS[1], ARGV[2]); ",
|
||||
"return 0; ",
|
||||
"else ",
|
||||
"redis.call('hdel', KEYS[1], ARGV[3]); ",
|
||||
"if (redis.call('hlen', KEYS[1]) == 1) then ",
|
||||
"redis.call('del', KEYS[1]); ",
|
||||
"redis.call('publish', KEYS[2], ARGV[1]); ",
|
||||
"else ",
|
||||
// has unlocked read-locks
|
||||
"redis.call('hset', KEYS[1], 'mode', 'read'); ",
|
||||
"end; ",
|
||||
"return 1; ",
|
||||
"end; ",
|
||||
"end; ",
|
||||
"end; ",
|
||||
"return nil;",
|
||||
}, "")
|
||||
|
||||
type redissionReadLocker struct {
|
||||
redissionLocker
|
||||
rwTimeoutTokenPrefix string
|
||||
prefixKey string
|
||||
}
|
||||
|
||||
func (rl *redissionReadLocker) Lock(ctx context.Context, timeout ...time.Duration) {
|
||||
if rl.exit == nil {
|
||||
rl.exit = make(chan struct{})
|
||||
}
|
||||
ttl, err := rl.tryLock()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if ttl <= 0 {
|
||||
rl.once.Do(func() {
|
||||
go rl.refreshLockTimeout()
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
submsg := make(chan struct{}, 1)
|
||||
defer close(submsg)
|
||||
sub := rl.client.Subscribe(rl.chankey)
|
||||
|
||||
defer sub.Close()
|
||||
go rl.subscribeLock(sub, submsg)
|
||||
// listen := rl.listenManager.Subscribe(rl.key, rl.token)
|
||||
// defer rl.listenManager.UnSubscribe(rl.key, rl.token)
|
||||
|
||||
timer := time.NewTimer(ttl)
|
||||
defer timer.Stop()
|
||||
var outimer *time.Timer
|
||||
if len(timeout) > 0 && timeout[0] > 0 {
|
||||
outimer = time.NewTimer(timeout[0])
|
||||
}
|
||||
LOOP:
|
||||
for {
|
||||
ttl, err = rl.tryLock()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if ttl <= 0 {
|
||||
rl.once.Do(func() {
|
||||
go rl.refreshLockTimeout()
|
||||
})
|
||||
return
|
||||
}
|
||||
if outimer != nil {
|
||||
select {
|
||||
case _, ok := <-submsg:
|
||||
if !timer.Stop() {
|
||||
<-timer.C
|
||||
}
|
||||
|
||||
if !ok {
|
||||
panic("lock listen release")
|
||||
}
|
||||
|
||||
timer.Reset(ttl)
|
||||
case <-ctx.Done():
|
||||
// break LOOP
|
||||
panic("lock context already release")
|
||||
case <-timer.C:
|
||||
timer.Reset(ttl)
|
||||
case <-outimer.C:
|
||||
if !timer.Stop() {
|
||||
<-timer.C
|
||||
}
|
||||
break LOOP
|
||||
}
|
||||
} else {
|
||||
select {
|
||||
case _, ok := <-submsg:
|
||||
if !timer.Stop() {
|
||||
<-timer.C
|
||||
}
|
||||
|
||||
if !ok {
|
||||
panic("lock listen release")
|
||||
}
|
||||
|
||||
timer.Reset(ttl)
|
||||
case <-ctx.Done():
|
||||
// break LOOP
|
||||
panic("lock context already release")
|
||||
case <-timer.C:
|
||||
timer.Reset(ttl)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (rl *redissionReadLocker) tryLock() (time.Duration, error) {
|
||||
writeLockToken := strings.Join([]string{rl.token, "write"}, ":")
|
||||
res := rl.client.Eval(rlockScript, []string{rl.key, rl.rwTimeoutTokenPrefix}, rl.lockLeaseTime, rl.token, writeLockToken)
|
||||
v, err := res.Result()
|
||||
if err != redis.Nil && err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if v == nil {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
return time.Duration(v.(int64)), nil
|
||||
}
|
||||
|
||||
func (rl *redissionReadLocker) refreshLockTimeout() {
|
||||
rl.logger.Debug("rlock: %s lock %s\n", zap.String("token", rl.token), zap.String("key", rl.key))
|
||||
lockTime := time.Duration(rl.lockLeaseTime/3) * time.Millisecond
|
||||
timer := time.NewTimer(lockTime)
|
||||
defer timer.Stop()
|
||||
LOOP:
|
||||
for {
|
||||
select {
|
||||
case <-timer.C:
|
||||
timer.Reset(lockTime)
|
||||
// update key expire time
|
||||
res := rl.client.Eval(rlockrefreshScript, []string{rl.key, rl.prefixKey}, rl.lockLeaseTime, rl.token)
|
||||
val, err := res.Int()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if val == 0 {
|
||||
rl.logger.Debug("not find the rlock key of self")
|
||||
break LOOP
|
||||
}
|
||||
case <-rl.exit:
|
||||
break LOOP
|
||||
|
||||
}
|
||||
}
|
||||
rl.logger.Debug("rlock: refresh routine release", zap.String("token", rl.token))
|
||||
}
|
||||
|
||||
func (rl *redissionReadLocker) UnLock() {
|
||||
res := rl.client.Eval(runlockScript, []string{rl.key, rl.chankey, rl.rwTimeoutTokenPrefix, rl.prefixKey}, unlockMessage, rl.token)
|
||||
val, err := res.Result()
|
||||
if err != redis.Nil && err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if val == nil {
|
||||
panic("attempt to unlock lock, not locked by current routine by lock id:" + rl.token)
|
||||
}
|
||||
rl.logger.Debug("lock: %s unlock %s\n", zap.String("token", rl.token), zap.String("key", rl.key))
|
||||
if val.(int64) == 1 {
|
||||
rl.cancelRefreshLockTime()
|
||||
}
|
||||
}
|
||||
|
||||
type redissionWriteLocker struct {
|
||||
redissionLocker
|
||||
}
|
||||
|
||||
func (rl *redissionWriteLocker) Lock(ctx context.Context, timeout ...time.Duration) {
|
||||
if rl.exit == nil {
|
||||
rl.exit = make(chan struct{})
|
||||
}
|
||||
ttl, err := rl.tryLock()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if ttl <= 0 {
|
||||
rl.once.Do(func() {
|
||||
go rl.refreshLockTimeout()
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
submsg := make(chan struct{}, 1)
|
||||
defer close(submsg)
|
||||
sub := rl.client.Subscribe(rl.chankey)
|
||||
defer sub.Close()
|
||||
go rl.subscribeLock(sub, submsg)
|
||||
// listen := rl.listenManager.Subscribe(rl.key, rl.token)
|
||||
// defer rl.listenManager.UnSubscribe(rl.key, rl.token)
|
||||
|
||||
timer := time.NewTimer(ttl)
|
||||
defer timer.Stop()
|
||||
// outimer 理解为如果超过这个时间没有获取到锁,就直接放弃
|
||||
var outimer *time.Timer
|
||||
if len(timeout) > 0 && timeout[0] > 0 {
|
||||
outimer = time.NewTimer(timeout[0])
|
||||
}
|
||||
LOOP:
|
||||
for {
|
||||
ttl, err = rl.tryLock()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if ttl <= 0 {
|
||||
rl.once.Do(func() {
|
||||
go rl.refreshLockTimeout()
|
||||
})
|
||||
return
|
||||
}
|
||||
if outimer != nil {
|
||||
select {
|
||||
case _, ok := <-submsg:
|
||||
if !timer.Stop() {
|
||||
<-timer.C
|
||||
}
|
||||
|
||||
if !ok {
|
||||
panic("lock listen release")
|
||||
}
|
||||
|
||||
timer.Reset(ttl)
|
||||
case <-ctx.Done():
|
||||
// break LOOP
|
||||
panic("lock context already release")
|
||||
case <-timer.C:
|
||||
timer.Reset(ttl)
|
||||
case <-outimer.C:
|
||||
if !timer.Stop() {
|
||||
<-timer.C
|
||||
}
|
||||
break LOOP
|
||||
}
|
||||
} else {
|
||||
select {
|
||||
case _, ok := <-submsg:
|
||||
if !timer.Stop() {
|
||||
<-timer.C
|
||||
}
|
||||
|
||||
if !ok {
|
||||
panic("lock listen release")
|
||||
}
|
||||
|
||||
timer.Reset(ttl)
|
||||
case <-ctx.Done():
|
||||
// break LOOP
|
||||
panic("lock context already release")
|
||||
case <-timer.C:
|
||||
timer.Reset(ttl)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (rl *redissionWriteLocker) tryLock() (time.Duration, error) {
|
||||
res := rl.client.Eval(wlockScript, []string{rl.key}, rl.lockLeaseTime, rl.token)
|
||||
v, err := res.Result()
|
||||
if err != redis.Nil && err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if v == nil {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
return time.Duration(v.(int64)), nil
|
||||
}
|
||||
|
||||
func (rl *redissionWriteLocker) UnLock() {
|
||||
res := rl.client.Eval(wunlockScript, []string{rl.key, rl.chankey}, unlockMessage, rl.lockLeaseTime, rl.token)
|
||||
val, err := res.Result()
|
||||
if err != redis.Nil && err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if val == nil {
|
||||
panic("attempt to unlock lock, not locked by current routine by lock id:" + rl.token)
|
||||
}
|
||||
rl.logger.Debug("lock: unlock", zap.String("token", rl.token), zap.String("key", rl.key))
|
||||
if val.(int64) == 1 {
|
||||
rl.cancelRefreshLockTime()
|
||||
}
|
||||
}
|
||||
|
||||
func GetReadLocker(client *redis.Client, ops *RedissionLockConfig) *redissionReadLocker {
|
||||
r := &redissionLocker{
|
||||
token: uuid.New().String(),
|
||||
client: client,
|
||||
exit: make(chan struct{}),
|
||||
once: &sync.Once{},
|
||||
}
|
||||
|
||||
if len(ops.Prefix) <= 0 {
|
||||
ops.Prefix = "redission-rwlock"
|
||||
}
|
||||
if len(ops.ChanPrefix) <= 0 {
|
||||
ops.ChanPrefix = "redission-rwlock-channel"
|
||||
}
|
||||
if ops.LockLeaseTime == 0 {
|
||||
r.lockLeaseTime = internalLockLeaseTime
|
||||
}
|
||||
r.key = strings.Join([]string{ops.Prefix, ops.Key}, ":")
|
||||
r.chankey = strings.Join([]string{ops.ChanPrefix, ops.Key}, ":")
|
||||
tkey := strings.Join([]string{"{", r.key, "}"}, "")
|
||||
return &redissionReadLocker{redissionLocker: *r, rwTimeoutTokenPrefix: strings.Join([]string{tkey, r.token, "rwlock_timeout"}, ":"), prefixKey: tkey}
|
||||
}
|
||||
|
||||
func GetWriteLocker(client *redis.Client, ops *RedissionLockConfig) *redissionWriteLocker {
|
||||
r := &redissionLocker{
|
||||
token: uuid.New().String(),
|
||||
client: client,
|
||||
exit: make(chan struct{}),
|
||||
once: &sync.Once{},
|
||||
}
|
||||
|
||||
if len(ops.Prefix) <= 0 {
|
||||
ops.Prefix = "redission-rwlock"
|
||||
}
|
||||
if len(ops.ChanPrefix) <= 0 {
|
||||
ops.ChanPrefix = "redission-rwlock-channel"
|
||||
}
|
||||
if ops.LockLeaseTime == 0 {
|
||||
r.lockLeaseTime = internalLockLeaseTime
|
||||
}
|
||||
r.key = strings.Join([]string{ops.Prefix, ops.Key}, ":")
|
||||
r.chankey = strings.Join([]string{ops.ChanPrefix, ops.Key}, ":")
|
||||
return &redissionWriteLocker{redissionLocker: *r}
|
||||
}
|
||||
6
go.mod
6
go.mod
|
|
@ -6,7 +6,9 @@ require (
|
|||
github.com/bitly/go-simplejson v0.5.1
|
||||
github.com/confluentinc/confluent-kafka-go v1.9.2
|
||||
github.com/gin-gonic/gin v1.10.0
|
||||
github.com/go-redis/redis v6.15.9+incompatible
|
||||
github.com/gofrs/uuid v4.4.0+incompatible
|
||||
github.com/google/uuid v1.4.0
|
||||
github.com/gorilla/websocket v1.5.3
|
||||
github.com/json-iterator/go v1.1.12
|
||||
github.com/natefinch/lumberjack v2.0.0+incompatible
|
||||
|
|
@ -16,6 +18,7 @@ require (
|
|||
github.com/swaggo/gin-swagger v1.6.0
|
||||
github.com/swaggo/swag v1.16.4
|
||||
go.uber.org/zap v1.27.0
|
||||
golang.org/x/sys v0.28.0
|
||||
gorm.io/driver/postgres v1.5.9
|
||||
gorm.io/gorm v1.25.12
|
||||
)
|
||||
|
|
@ -54,6 +57,8 @@ require (
|
|||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/onsi/ginkgo v1.16.5 // indirect
|
||||
github.com/onsi/gomega v1.18.1 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
|
||||
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||
|
|
@ -70,7 +75,6 @@ require (
|
|||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
|
||||
golang.org/x/net v0.32.0 // indirect
|
||||
golang.org/x/sync v0.10.0 // indirect
|
||||
golang.org/x/sys v0.28.0 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
golang.org/x/tools v0.28.0 // indirect
|
||||
google.golang.org/protobuf v1.35.2 // indirect
|
||||
|
|
|
|||
45
go.sum
45
go.sum
|
|
@ -52,6 +52,8 @@ github.com/frankban/quicktest v1.10.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq
|
|||
github.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||
github.com/gabriel-vasile/mimetype v1.4.7 h1:SKFKl7kD0RiPdbht0s7hFtjl489WcQ1VyPW8ZzUMYCA=
|
||||
|
|
@ -79,6 +81,9 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn
|
|||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.23.0 h1:/PwmTwZhS0dPkav3cdK9kV1FsAmrL8sThn8IHr/sO+o=
|
||||
github.com/go-playground/validator/v10 v10.23.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||
github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg=
|
||||
github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
|
||||
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA=
|
||||
|
|
@ -112,9 +117,12 @@ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
|||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20211008130755-947d60d73cc0/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
|
||||
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
|
|
@ -122,7 +130,9 @@ github.com/hamba/avro v1.5.6/go.mod h1:3vNT0RLXXpFm2Tb/5KC71ZRJlOroggq1Rcitb6k4F
|
|||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/heetch/avro v0.3.1/go.mod h1:4xn38Oz/+hiEUTpbVfGVLfvOg0yKLlRP7Q9+gJJILgA=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0/go.mod h1:N0Wam8K1arqPXNWjMo21EXnBPOPp36vB07FNRdD2geA=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
|
||||
github.com/invopop/jsonschema v0.4.0/go.mod h1:O9uiLokuu0+MGFlyiaqtWxwqJm41/+8Nj0lD7A36YH0=
|
||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||
|
|
@ -186,6 +196,20 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY
|
|||
github.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4UpgHp07nNdFX7mqFfM=
|
||||
github.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk=
|
||||
github.com/nrwiersma/avro-benchmarks v0.0.0-20210913175520-21aec48c8f76/go.mod h1:iKyFMidsk/sVYONJRE372sJuX/QTRPacU7imPqqsu7g=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
|
||||
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
||||
github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
|
||||
github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
|
||||
github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
|
||||
github.com/panjf2000/ants/v2 v2.10.0 h1:zhRg1pQUtkyRiOFo2Sbqwjp0GfBNo9cUY2/Grpx1p+8=
|
||||
github.com/panjf2000/ants/v2 v2.10.0/go.mod h1:7ZxyxsqE4vvW0M7LSD8aI3cKwgFhBHbxnlN8mDqHa1I=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
|
||||
|
|
@ -242,6 +266,7 @@ github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2
|
|||
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
||||
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
|
|
@ -265,11 +290,13 @@ golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTk
|
|||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
|
||||
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
|
|
@ -277,11 +304,13 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
|
|||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200505041828-1ed23360d12c/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI=
|
||||
|
|
@ -293,22 +322,31 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ
|
|||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
|
|
@ -322,6 +360,7 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
|||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
|
|
@ -333,6 +372,7 @@ golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3
|
|||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8=
|
||||
golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw=
|
||||
|
|
@ -378,6 +418,7 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN
|
|||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/errgo.v1 v1.0.0/go.mod h1:CxwszS/Xz1C49Ucd2i6Zil5UToP1EmyrFhKaMVbg1mk=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/httprequest.v1 v1.2.1/go.mod h1:x2Otw96yda5+8+6ZeWwHIJTFkEHWP/qP8pJOzqEtWPM=
|
||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
|
|
@ -385,10 +426,14 @@ gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3M
|
|||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
|
||||
gopkg.in/retry.v1 v1.0.3/go.mod h1:FJkXmWiMaAo7xB+xhvDF59zhfjDWyzmyAxiT4dB688g=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
|
|
|||
|
|
@ -1,52 +0,0 @@
|
|||
package sharememory
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// 打开文件
|
||||
file, err := os.OpenFile("testfile.txt", os.O_RDWR|os.O_CREATE, 0o666)
|
||||
if err != nil {
|
||||
fmt.Println("Error opening file:", err)
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// 加独占锁
|
||||
fmt.Println("Acquiring exclusive lock...")
|
||||
err = unix.Flock(int(file.Fd()), unix.LOCK_EX)
|
||||
if err != nil {
|
||||
fmt.Println("Error acquiring exclusive lock:", err)
|
||||
return
|
||||
}
|
||||
defer unix.Flock(int(file.Fd()), unix.LOCK_UN) // 释放锁
|
||||
|
||||
fmt.Println("Exclusive lock acquired. Writing to file...")
|
||||
// 这里可以添加写文件的逻辑
|
||||
fmt.Println("Writing complete.")
|
||||
|
||||
// 打开文件
|
||||
file, err = os.OpenFile("testfile.txt", os.O_RDONLY, 0o666)
|
||||
if err != nil {
|
||||
fmt.Println("Error opening file:", err)
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// 加共享锁
|
||||
fmt.Println("Acquiring shared lock...")
|
||||
err = unix.Flock(int(file.Fd()), unix.LOCK_SH)
|
||||
if err != nil {
|
||||
fmt.Println("Error acquiring shared lock:", err)
|
||||
return
|
||||
}
|
||||
defer unix.Flock(int(file.Fd()), unix.LOCK_UN) // 释放锁
|
||||
|
||||
fmt.Println("Shared lock acquired. Reading from file...")
|
||||
// 这里可以添加读文件的逻辑
|
||||
fmt.Println("Reading complete.")
|
||||
}
|
||||
Loading…
Reference in New Issue