diff --git a/README.md b/README.md index b247ca2..b52488c 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,3 @@ # ModelRT + [![Build Status](http://192.168.46.100:4080/api/badges/CL-Softwares/modelRT/status.svg)](http://192.168.46.100:4080/CL-Softwares/modelRT) \ No newline at end of file diff --git a/distributedlock/luascript/rlock_script.go b/distributedlock/luascript/rlock_script.go new file mode 100644 index 0000000..d2d72f1 --- /dev/null +++ b/distributedlock/luascript/rlock_script.go @@ -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; +` diff --git a/distributedlock/redis_lock.go b/distributedlock/redis_lock.go new file mode 100644 index 0000000..e66c03c --- /dev/null +++ b/distributedlock/redis_lock.go @@ -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 +} diff --git a/distributedlock/redis_rwlock.go b/distributedlock/redis_rwlock.go new file mode 100644 index 0000000..cd15035 --- /dev/null +++ b/distributedlock/redis_rwlock.go @@ -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} +} diff --git a/go.mod b/go.mod index e5f2b8c..3d655e8 100644 --- a/go.mod +++ b/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 diff --git a/go.sum b/go.sum index da45600..86f0495 100644 --- a/go.sum +++ b/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= diff --git a/share_memory/file_lock.go b/share_memory/file_lock.go deleted file mode 100644 index 9a169e7..0000000 --- a/share_memory/file_lock.go +++ /dev/null @@ -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.") -} diff --git a/share_memory/share_memeory.go b/sharememory/share_memeory.go similarity index 100% rename from share_memory/share_memeory.go rename to sharememory/share_memeory.go