refactor(locker params): optimize of redis locker params

1.optimize locker unexport params
    2.optimize locker logging output
    3.optimize locker init process
This commit is contained in:
douxu 2025-04-18 14:02:03 +08:00
parent d27a9bbafa
commit 310f4c043c
3 changed files with 154 additions and 151 deletions

View File

@ -35,15 +35,15 @@ type RedissionLockConfig struct {
type redissionLocker struct { type redissionLocker struct {
lockLeaseTime uint64 lockLeaseTime uint64
token string Token string
key string Key string
waitChanKey string waitChanKey string
needRefresh bool needRefresh bool
refreshExitChan chan struct{} refreshExitChan chan struct{}
subExitChan chan struct{} subExitChan chan struct{}
client *redis.Client client *redis.Client
refreshOnce *sync.Once refreshOnce *sync.Once
logger *zap.Logger Logger *zap.Logger
} }
func (rl *redissionLocker) Lock(ctx context.Context, timeout ...time.Duration) error { func (rl *redissionLocker) Lock(ctx context.Context, timeout ...time.Duration) error {
@ -52,7 +52,7 @@ func (rl *redissionLocker) Lock(ctx context.Context, timeout ...time.Duration) e
} }
result := rl.tryLock(ctx).(*constant.RedisResult) result := rl.tryLock(ctx).(*constant.RedisResult)
if result.Code == constant.UnknownInternalError { if result.Code == constant.UnknownInternalError {
rl.logger.Error(result.OutputResultMessage()) rl.Logger.Error(result.OutputResultMessage())
return fmt.Errorf("get lock failed:%w", result) return fmt.Errorf("get lock failed:%w", result)
} }
@ -77,23 +77,23 @@ func (rl *redissionLocker) Lock(ctx context.Context, timeout ...time.Duration) e
case _, ok := <-subMsg: case _, ok := <-subMsg:
if !ok { if !ok {
err := errors.New("failed to read the lock waiting for for the channel message") err := errors.New("failed to read the lock waiting for for the channel message")
rl.logger.Error("failed to read the lock waiting for for the channel message") rl.Logger.Error("failed to read the lock waiting for for the channel message")
return err return err
} }
resultErr := rl.tryLock(ctx).(*constant.RedisResult) resultErr := rl.tryLock(ctx).(*constant.RedisResult)
if (resultErr.Code == constant.LockFailure) || (resultErr.Code == constant.UnknownInternalError) { if (resultErr.Code == constant.LockFailure) || (resultErr.Code == constant.UnknownInternalError) {
rl.logger.Info(resultErr.OutputResultMessage()) rl.Logger.Info(resultErr.OutputResultMessage())
continue continue
} }
if resultErr.Code == constant.LockSuccess { if resultErr.Code == constant.LockSuccess {
rl.logger.Info(resultErr.OutputResultMessage()) rl.Logger.Info(resultErr.OutputResultMessage())
return nil return nil
} }
case <-acquireTimer.C: case <-acquireTimer.C:
err := errors.New("the waiting time for obtaining the lock operation has timed out") err := errors.New("the waiting time for obtaining the lock operation has timed out")
rl.logger.Info("the waiting time for obtaining the lock operation has timed out") rl.Logger.Info("the waiting time for obtaining the lock operation has timed out")
return err return err
} }
} }
@ -105,7 +105,7 @@ func (rl *redissionLocker) subscribeLock(sub *redis.PubSub, subMsgChan chan stru
if sub == nil || subMsgChan == nil { if sub == nil || subMsgChan == nil {
return return
} }
rl.logger.Info("lock: enter sub routine", zap.String("token", rl.token)) rl.Logger.Info("lock: enter sub routine", zap.String("token", rl.Token))
for { for {
select { select {
@ -126,7 +126,7 @@ ARGV[1]:锁的过期时间lockLeaseTime,单位为秒。
ARGV[2]:当前客户端的唯一标识token,用于区分不同的客户端 ARGV[2]:当前客户端的唯一标识token,用于区分不同的客户端
*/ */
func (rl *redissionLocker) refreshLockTimeout(ctx context.Context) { func (rl *redissionLocker) refreshLockTimeout(ctx context.Context) {
rl.logger.Info("lock refresh by key and token", zap.String("token", rl.token), zap.String("key", rl.key)) rl.Logger.Info("lock refresh by key and token", zap.String("token", rl.Token), zap.String("key", rl.Key))
lockTime := time.Duration(rl.lockLeaseTime/3) * time.Second lockTime := time.Duration(rl.lockLeaseTime/3) * time.Second
timer := time.NewTimer(lockTime) timer := time.NewTimer(lockTime)
@ -136,20 +136,20 @@ func (rl *redissionLocker) refreshLockTimeout(ctx context.Context) {
select { select {
case <-timer.C: case <-timer.C:
// extend key lease time // extend key lease time
res := rl.client.Eval(ctx, luascript.RefreshLockScript, []string{rl.key}, rl.lockLeaseTime, rl.token) res := rl.client.Eval(ctx, luascript.RefreshLockScript, []string{rl.Key}, rl.lockLeaseTime, rl.Token)
val, err := res.Int() val, err := res.Int()
if err != redis.Nil && err != nil { if err != redis.Nil && err != nil {
rl.logger.Info("lock refresh failed", zap.String("token", rl.token), zap.String("key", rl.key), zap.Error(err)) rl.Logger.Info("lock refresh failed", zap.String("token", rl.Token), zap.String("key", rl.Key), zap.Error(err))
return return
} }
if constant.RedisCode(val) == constant.RefreshLockFailure { if constant.RedisCode(val) == constant.RefreshLockFailure {
rl.logger.Error("lock refreash failed,can not find the lock by key and token", zap.String("token", rl.token), zap.String("key", rl.key)) rl.Logger.Error("lock refreash failed,can not find the lock by key and token", zap.String("token", rl.Token), zap.String("key", rl.Key))
break break
} }
if constant.RedisCode(val) == constant.RefreshLockSuccess { if constant.RedisCode(val) == constant.RefreshLockSuccess {
rl.logger.Info("lock refresh success by key and token", zap.String("token", rl.token), zap.String("key", rl.key)) rl.Logger.Info("lock refresh success by key and token", zap.String("token", rl.Token), zap.String("key", rl.Key))
} }
timer.Reset(lockTime) timer.Reset(lockTime)
case <-rl.refreshExitChan: case <-rl.refreshExitChan:
@ -169,7 +169,7 @@ func (rl *redissionLocker) closeSub(sub *redis.PubSub, noticeChan chan struct{})
if sub != nil { if sub != nil {
err := sub.Close() err := sub.Close()
if err != nil { if err != nil {
rl.logger.Error("close sub failed", zap.String("token", rl.token), zap.String("key", rl.key), zap.Error(err)) rl.Logger.Error("close sub failed", zap.String("token", rl.Token), zap.String("key", rl.Key), zap.Error(err))
} }
} }
@ -185,7 +185,7 @@ ARGV[2]:当前客户端的唯一标识token,用于区分不同的客户端
*/ */
func (rl *redissionLocker) tryLock(ctx context.Context) error { func (rl *redissionLocker) tryLock(ctx context.Context) error {
lockType := constant.LockType lockType := constant.LockType
res := rl.client.Eval(ctx, luascript.LockScript, []string{rl.key}, rl.lockLeaseTime, rl.token) res := rl.client.Eval(ctx, luascript.LockScript, []string{rl.Key}, rl.lockLeaseTime, rl.Token)
val, err := res.Int() val, err := res.Int()
if err != redis.Nil && err != nil { if err != redis.Nil && err != nil {
return constant.NewRedisResult(constant.UnknownInternalError, lockType, err.Error()) return constant.NewRedisResult(constant.UnknownInternalError, lockType, err.Error())
@ -200,10 +200,10 @@ ARGV[1]:解锁消息unlockMessage,用于通知其他客户端锁已释放
ARGV[2]:当前客户端的唯一标识token,用于区分不同的客户端 ARGV[2]:当前客户端的唯一标识token,用于区分不同的客户端
*/ */
func (rl *redissionLocker) UnLock(ctx context.Context) error { func (rl *redissionLocker) UnLock(ctx context.Context) error {
res := rl.client.Eval(ctx, luascript.UnLockScript, []string{rl.key, rl.waitChanKey}, unlockMessage, rl.token) res := rl.client.Eval(ctx, luascript.UnLockScript, []string{rl.Key, rl.waitChanKey}, unlockMessage, rl.Token)
val, err := res.Int() val, err := res.Int()
if err != redis.Nil && err != nil { if err != redis.Nil && err != nil {
rl.logger.Info("unlock lock failed", zap.String("token", rl.token), zap.String("key", rl.key), zap.Error(err)) rl.Logger.Info("unlock lock failed", zap.String("token", rl.Token), zap.String("key", rl.Key), zap.Error(err))
return fmt.Errorf("unlock lock failed:%w", constant.NewRedisResult(constant.UnknownInternalError, constant.UnLockType, err.Error())) return fmt.Errorf("unlock lock failed:%w", constant.NewRedisResult(constant.UnknownInternalError, constant.UnLockType, err.Error()))
} }
@ -212,12 +212,12 @@ func (rl *redissionLocker) UnLock(ctx context.Context) error {
rl.cancelRefreshLockTime() rl.cancelRefreshLockTime()
} }
rl.logger.Info("unlock lock success", zap.String("token", rl.token), zap.String("key", rl.key)) rl.Logger.Info("unlock lock success", zap.String("token", rl.Token), zap.String("key", rl.Key))
return nil return nil
} }
if constant.RedisCode(val) == constant.UnLocakFailureWithLockOccupancy { if constant.RedisCode(val) == constant.UnLocakFailureWithLockOccupancy {
rl.logger.Info("unlock lock failed", zap.String("token", rl.token), zap.String("key", rl.key)) rl.Logger.Info("unlock lock failed", zap.String("token", rl.Token), zap.String("key", rl.Key))
return fmt.Errorf("unlock lock failed:%w", constant.NewRedisResult(constant.UnLocakFailureWithLockOccupancy, constant.UnLockType, "")) return fmt.Errorf("unlock lock failed:%w", constant.NewRedisResult(constant.UnLocakFailureWithLockOccupancy, constant.UnLockType, ""))
} }
return nil return nil
@ -246,13 +246,13 @@ func GetLocker(client *redis.Client, ops *RedissionLockConfig) *redissionLocker
} }
r := &redissionLocker{ r := &redissionLocker{
token: ops.Token, Token: ops.Token,
key: strings.Join([]string{ops.Prefix, ops.Key}, ":"), Key: strings.Join([]string{ops.Prefix, ops.Key}, ":"),
waitChanKey: strings.Join([]string{ops.ChanPrefix, ops.Key, "wait"}, ":"), waitChanKey: strings.Join([]string{ops.ChanPrefix, ops.Key, "wait"}, ":"),
needRefresh: ops.NeedRefresh, needRefresh: ops.NeedRefresh,
client: client, client: client,
refreshExitChan: make(chan struct{}), refreshExitChan: make(chan struct{}),
logger: logger.GetLoggerInstance(), Logger: logger.GetLoggerInstance(),
} }
return r return r
} }

View File

@ -21,13 +21,13 @@ type RedissionRWLocker struct {
redissionLocker redissionLocker
writeWaitChanKey string writeWaitChanKey string
readWaitChanKey string readWaitChanKey string
rwTokenTimeoutPrefix string RWTokenTimeoutPrefix string
} }
func (rl *RedissionRWLocker) RLock(ctx context.Context, timeout ...time.Duration) error { func (rl *RedissionRWLocker) RLock(ctx context.Context, timeout ...time.Duration) error {
result := rl.tryRLock(ctx).(*constant.RedisResult) result := rl.tryRLock(ctx).(*constant.RedisResult)
if result.Code == constant.UnknownInternalError { if result.Code == constant.UnknownInternalError {
rl.logger.Error(result.OutputResultMessage()) rl.Logger.Error(result.OutputResultMessage())
return fmt.Errorf("get read lock failed:%w", result) return fmt.Errorf("get read lock failed:%w", result)
} }
@ -42,7 +42,7 @@ func (rl *RedissionRWLocker) RLock(ctx context.Context, timeout ...time.Duration
go rl.refreshLockTimeout(ctx) go rl.refreshLockTimeout(ctx)
}) })
} }
rl.logger.Info("success get the read lock by key and token", zap.String("key", rl.key), zap.String("token", rl.token)) rl.Logger.Info("success get the read lock by key and token", zap.String("key", rl.Key), zap.String("token", rl.Token))
return nil return nil
} }
@ -61,18 +61,18 @@ func (rl *RedissionRWLocker) RLock(ctx context.Context, timeout ...time.Duration
case _, ok := <-subMsgChan: case _, ok := <-subMsgChan:
if !ok { if !ok {
err := errors.New("failed to read the read lock waiting for for the channel message") err := errors.New("failed to read the read lock waiting for for the channel message")
rl.logger.Error("failed to read the read lock waiting for for the channel message") rl.Logger.Error("failed to read the read lock waiting for for the channel message")
return err return err
} }
result := rl.tryRLock(ctx).(*constant.RedisResult) result := rl.tryRLock(ctx).(*constant.RedisResult)
if (result.Code == constant.RLockFailureWithWLockOccupancy) || (result.Code == constant.UnknownInternalError) { if (result.Code == constant.RLockFailureWithWLockOccupancy) || (result.Code == constant.UnknownInternalError) {
rl.logger.Info(result.OutputResultMessage()) rl.Logger.Info(result.OutputResultMessage())
continue continue
} }
if result.Code == constant.LockSuccess { if result.Code == constant.LockSuccess {
rl.logger.Info(result.OutputResultMessage()) rl.Logger.Info(result.OutputResultMessage())
rl.closeSub(sub, rl.subExitChan) rl.closeSub(sub, rl.subExitChan)
if rl.needRefresh { if rl.needRefresh {
@ -88,7 +88,7 @@ func (rl *RedissionRWLocker) RLock(ctx context.Context, timeout ...time.Duration
return nil return nil
} }
case <-acquireTimer.C: case <-acquireTimer.C:
rl.logger.Info("the waiting time for obtaining the read lock operation has timed out") rl.Logger.Info("the waiting time for obtaining the read lock operation has timed out")
rl.closeSub(sub, rl.subExitChan) rl.closeSub(sub, rl.subExitChan)
// after acquire lock timeout,notice the sub channel to close // after acquire lock timeout,notice the sub channel to close
return constant.AcquireTimeoutErr return constant.AcquireTimeoutErr
@ -101,7 +101,7 @@ func (rl *RedissionRWLocker) RLock(ctx context.Context, timeout ...time.Duration
func (rl *RedissionRWLocker) tryRLock(ctx context.Context) error { func (rl *RedissionRWLocker) tryRLock(ctx context.Context) error {
lockType := constant.LockType lockType := constant.LockType
res := rl.client.Eval(ctx, luascript.RLockScript, []string{rl.key, rl.rwTokenTimeoutPrefix}, rl.lockLeaseTime, rl.token) res := rl.client.Eval(ctx, luascript.RLockScript, []string{rl.Key, rl.RWTokenTimeoutPrefix}, rl.lockLeaseTime, rl.Token)
val, err := res.Int() val, err := res.Int()
if err != redis.Nil && err != nil { if err != redis.Nil && err != nil {
return constant.NewRedisResult(constant.UnknownInternalError, lockType, err.Error()) return constant.NewRedisResult(constant.UnknownInternalError, lockType, err.Error())
@ -110,7 +110,7 @@ func (rl *RedissionRWLocker) tryRLock(ctx context.Context) error {
} }
func (rl *RedissionRWLocker) refreshLockTimeout(ctx context.Context) { func (rl *RedissionRWLocker) refreshLockTimeout(ctx context.Context) {
rl.logger.Info("lock refresh by key and token", zap.String("token", rl.token), zap.String("key", rl.key)) rl.Logger.Info("lock refresh by key and token", zap.String("token", rl.Token), zap.String("key", rl.Key))
lockTime := time.Duration(rl.lockLeaseTime/3) * time.Second lockTime := time.Duration(rl.lockLeaseTime/3) * time.Second
timer := time.NewTimer(lockTime) timer := time.NewTimer(lockTime)
@ -120,20 +120,20 @@ func (rl *RedissionRWLocker) refreshLockTimeout(ctx context.Context) {
select { select {
case <-timer.C: case <-timer.C:
// extend key lease time // extend key lease time
res := rl.client.Eval(ctx, luascript.RefreshRWLockScript, []string{rl.key, rl.rwTokenTimeoutPrefix}, rl.lockLeaseTime, rl.token) res := rl.client.Eval(ctx, luascript.RefreshRWLockScript, []string{rl.Key, rl.RWTokenTimeoutPrefix}, rl.lockLeaseTime, rl.Token)
val, err := res.Int() val, err := res.Int()
if err != redis.Nil && err != nil { if err != redis.Nil && err != nil {
rl.logger.Info("lock refresh failed", zap.String("token", rl.token), zap.String("key", rl.key), zap.Error(err)) rl.Logger.Info("lock refresh failed", zap.String("token", rl.Token), zap.String("key", rl.Key), zap.Error(err))
return return
} }
if constant.RedisCode(val) == constant.RefreshLockFailure { if constant.RedisCode(val) == constant.RefreshLockFailure {
rl.logger.Error("lock refreash failed,can not find the read lock by key and token", zap.String("rwTokenPrefix", rl.rwTokenTimeoutPrefix), zap.String("token", rl.token), zap.String("key", rl.key)) rl.Logger.Error("lock refreash failed,can not find the read lock by key and token", zap.String("rwTokenPrefix", rl.RWTokenTimeoutPrefix), zap.String("token", rl.Token), zap.String("key", rl.Key))
return return
} }
if constant.RedisCode(val) == constant.RefreshLockSuccess { if constant.RedisCode(val) == constant.RefreshLockSuccess {
rl.logger.Info("lock refresh success by key and token", zap.String("token", rl.token), zap.String("key", rl.key)) rl.Logger.Info("lock refresh success by key and token", zap.String("token", rl.Token), zap.String("key", rl.Key))
} }
timer.Reset(lockTime) timer.Reset(lockTime)
case <-rl.refreshExitChan: case <-rl.refreshExitChan:
@ -143,11 +143,11 @@ func (rl *RedissionRWLocker) refreshLockTimeout(ctx context.Context) {
} }
func (rl *RedissionRWLocker) UnRLock(ctx context.Context) error { func (rl *RedissionRWLocker) UnRLock(ctx context.Context) error {
rl.logger.Info("unlock RLock by key and token", zap.String("key", rl.key), zap.String("token", rl.token)) rl.Logger.Info("unlock RLock by key and token", zap.String("key", rl.Key), zap.String("token", rl.Token))
res := rl.client.Eval(ctx, luascript.UnRLockScript, []string{rl.key, rl.rwTokenTimeoutPrefix, rl.writeWaitChanKey}, unlockMessage, rl.token) res := rl.client.Eval(ctx, luascript.UnRLockScript, []string{rl.Key, rl.RWTokenTimeoutPrefix, rl.writeWaitChanKey}, unlockMessage, rl.Token)
val, err := res.Int() val, err := res.Int()
if err != redis.Nil && err != nil { if err != redis.Nil && err != nil {
rl.logger.Info("unlock read lock failed", zap.String("token", rl.token), zap.String("key", rl.key), zap.Error(err)) rl.Logger.Info("unlock read lock failed", zap.String("token", rl.Token), zap.String("key", rl.Key), zap.Error(err))
return fmt.Errorf("unlock read lock failed:%w", constant.NewRedisResult(constant.UnknownInternalError, constant.UnRLockType, err.Error())) return fmt.Errorf("unlock read lock failed:%w", constant.NewRedisResult(constant.UnknownInternalError, constant.UnRLockType, err.Error()))
} }
@ -156,12 +156,12 @@ func (rl *RedissionRWLocker) UnRLock(ctx context.Context) error {
rl.cancelRefreshLockTime() rl.cancelRefreshLockTime()
} }
rl.logger.Info("unlock read lock success", zap.String("token", rl.token), zap.String("key", rl.key)) rl.Logger.Info("unlock read lock success", zap.String("token", rl.Token), zap.String("key", rl.Key))
return nil return nil
} }
if constant.RedisCode(val) == constant.UnRLockFailureWithWLockOccupancy { if constant.RedisCode(val) == constant.UnRLockFailureWithWLockOccupancy {
rl.logger.Info("unlock read lock failed", zap.String("token", rl.token), zap.String("key", rl.key)) rl.Logger.Info("unlock read lock failed", zap.String("token", rl.Token), zap.String("key", rl.Key))
return fmt.Errorf("unlock read lock failed:%w", constant.NewRedisResult(constant.UnRLockFailureWithWLockOccupancy, constant.UnRLockType, "")) return fmt.Errorf("unlock read lock failed:%w", constant.NewRedisResult(constant.UnRLockFailureWithWLockOccupancy, constant.UnRLockType, ""))
} }
return nil return nil
@ -170,7 +170,7 @@ func (rl *RedissionRWLocker) UnRLock(ctx context.Context) error {
func (rl *RedissionRWLocker) WLock(ctx context.Context, timeout ...time.Duration) error { func (rl *RedissionRWLocker) WLock(ctx context.Context, timeout ...time.Duration) error {
result := rl.tryWLock(ctx).(*constant.RedisResult) result := rl.tryWLock(ctx).(*constant.RedisResult)
if result.Code == constant.UnknownInternalError { if result.Code == constant.UnknownInternalError {
rl.logger.Error(result.OutputResultMessage()) rl.Logger.Error(result.OutputResultMessage())
return fmt.Errorf("get write lock failed:%w", result) return fmt.Errorf("get write lock failed:%w", result)
} }
@ -185,7 +185,7 @@ func (rl *RedissionRWLocker) WLock(ctx context.Context, timeout ...time.Duration
go rl.refreshLockTimeout(ctx) go rl.refreshLockTimeout(ctx)
}) })
} }
rl.logger.Info("success get the write lock by key and token", zap.String("key", rl.key), zap.String("token", rl.token)) rl.Logger.Info("success get the write lock by key and token", zap.String("key", rl.Key), zap.String("token", rl.Token))
return nil return nil
} }
@ -204,18 +204,18 @@ func (rl *RedissionRWLocker) WLock(ctx context.Context, timeout ...time.Duration
case _, ok := <-subMsgChan: case _, ok := <-subMsgChan:
if !ok { if !ok {
err := errors.New("failed to read the write lock waiting for for the channel message") err := errors.New("failed to read the write lock waiting for for the channel message")
rl.logger.Error("failed to read the read lock waiting for for the channel message") rl.Logger.Error("failed to read the read lock waiting for for the channel message")
return err return err
} }
result := rl.tryWLock(ctx).(*constant.RedisResult) result := rl.tryWLock(ctx).(*constant.RedisResult)
if (result.Code == constant.UnknownInternalError) || (result.Code == constant.WLockFailureWithRLockOccupancy) || (result.Code == constant.WLockFailureWithWLockOccupancy) || (result.Code == constant.WLockFailureWithNotFirstPriority) { if (result.Code == constant.UnknownInternalError) || (result.Code == constant.WLockFailureWithRLockOccupancy) || (result.Code == constant.WLockFailureWithWLockOccupancy) || (result.Code == constant.WLockFailureWithNotFirstPriority) {
rl.logger.Info(result.OutputResultMessage()) rl.Logger.Info(result.OutputResultMessage())
continue continue
} }
if result.Code == constant.LockSuccess { if result.Code == constant.LockSuccess {
rl.logger.Info(result.OutputResultMessage()) rl.Logger.Info(result.OutputResultMessage())
rl.closeSub(sub, rl.subExitChan) rl.closeSub(sub, rl.subExitChan)
if rl.needRefresh { if rl.needRefresh {
@ -231,7 +231,7 @@ func (rl *RedissionRWLocker) WLock(ctx context.Context, timeout ...time.Duration
return nil return nil
} }
case <-acquireTimer.C: case <-acquireTimer.C:
rl.logger.Info("the waiting time for obtaining the write lock operation has timed out") rl.Logger.Info("the waiting time for obtaining the write lock operation has timed out")
rl.closeSub(sub, rl.subExitChan) rl.closeSub(sub, rl.subExitChan)
// after acquire lock timeout,notice the sub channel to close // after acquire lock timeout,notice the sub channel to close
return constant.AcquireTimeoutErr return constant.AcquireTimeoutErr
@ -244,7 +244,7 @@ func (rl *RedissionRWLocker) WLock(ctx context.Context, timeout ...time.Duration
func (rl *RedissionRWLocker) tryWLock(ctx context.Context) error { func (rl *RedissionRWLocker) tryWLock(ctx context.Context) error {
lockType := constant.LockType lockType := constant.LockType
res := rl.client.Eval(ctx, luascript.WLockScript, []string{rl.key, rl.rwTokenTimeoutPrefix}, rl.lockLeaseTime, rl.token) res := rl.client.Eval(ctx, luascript.WLockScript, []string{rl.Key, rl.RWTokenTimeoutPrefix}, rl.lockLeaseTime, rl.Token)
val, err := res.Int() val, err := res.Int()
if err != redis.Nil && err != nil { if err != redis.Nil && err != nil {
return constant.NewRedisResult(constant.UnknownInternalError, lockType, err.Error()) return constant.NewRedisResult(constant.UnknownInternalError, lockType, err.Error())
@ -253,10 +253,10 @@ func (rl *RedissionRWLocker) tryWLock(ctx context.Context) error {
} }
func (rl *RedissionRWLocker) UnWLock(ctx context.Context) error { func (rl *RedissionRWLocker) UnWLock(ctx context.Context) error {
res := rl.client.Eval(ctx, luascript.UnWLockScript, []string{rl.key, rl.rwTokenTimeoutPrefix, rl.writeWaitChanKey, rl.readWaitChanKey}, unlockMessage, rl.token) res := rl.client.Eval(ctx, luascript.UnWLockScript, []string{rl.Key, rl.RWTokenTimeoutPrefix, rl.writeWaitChanKey, rl.readWaitChanKey}, unlockMessage, rl.Token)
val, err := res.Int() val, err := res.Int()
if err != redis.Nil && err != nil { if err != redis.Nil && err != nil {
rl.logger.Error("unlock write lock failed", zap.String("token", rl.token), zap.String("key", rl.key), zap.Error(err)) rl.Logger.Error("unlock write lock failed", zap.String("token", rl.Token), zap.String("key", rl.Key), zap.Error(err))
return fmt.Errorf("unlock write lock failed:%w", constant.NewRedisResult(constant.UnknownInternalError, constant.UnWLockType, err.Error())) return fmt.Errorf("unlock write lock failed:%w", constant.NewRedisResult(constant.UnknownInternalError, constant.UnWLockType, err.Error()))
} }
@ -264,12 +264,12 @@ func (rl *RedissionRWLocker) UnWLock(ctx context.Context) error {
if rl.needRefresh && (constant.RedisCode(val) == constant.UnLockSuccess) { if rl.needRefresh && (constant.RedisCode(val) == constant.UnLockSuccess) {
rl.cancelRefreshLockTime() rl.cancelRefreshLockTime()
} }
rl.logger.Info("unlock write lock success", zap.String("token", rl.token), zap.String("key", rl.key)) rl.Logger.Info("unlock write lock success", zap.String("token", rl.Token), zap.String("key", rl.Key))
return nil return nil
} }
if (constant.RedisCode(val) == constant.UnWLockFailureWithRLockOccupancy) || (constant.RedisCode(val) == constant.UnWLockFailureWithWLockOccupancy) { if (constant.RedisCode(val) == constant.UnWLockFailureWithRLockOccupancy) || (constant.RedisCode(val) == constant.UnWLockFailureWithWLockOccupancy) {
rl.logger.Info("unlock write lock failed", zap.String("token", rl.token), zap.String("key", rl.key)) rl.Logger.Info("unlock write lock failed", zap.String("token", rl.Token), zap.String("key", rl.Key))
return fmt.Errorf("unlock write lock failed:%w", constant.NewRedisResult(constant.RedisCode(val), constant.UnWLockType, "")) return fmt.Errorf("unlock write lock failed:%w", constant.NewRedisResult(constant.RedisCode(val), constant.UnWLockType, ""))
} }
return nil return nil
@ -302,20 +302,20 @@ func GetRWLocker(client *redis.Client, conf *RedissionLockConfig) *RedissionRWLo
} }
r := &redissionLocker{ r := &redissionLocker{
token: conf.Token, Token: conf.Token,
key: strings.Join([]string{conf.Prefix, conf.Key}, ":"), Key: strings.Join([]string{conf.Prefix, conf.Key}, ":"),
needRefresh: conf.NeedRefresh, needRefresh: conf.NeedRefresh,
lockLeaseTime: conf.LockLeaseTime, lockLeaseTime: conf.LockLeaseTime,
client: client, client: client,
refreshOnce: &sync.Once{}, refreshOnce: &sync.Once{},
logger: logger.GetLoggerInstance(), Logger: logger.GetLoggerInstance(),
} }
rwLocker := &RedissionRWLocker{ rwLocker := &RedissionRWLocker{
redissionLocker: *r, redissionLocker: *r,
writeWaitChanKey: strings.Join([]string{conf.ChanPrefix, conf.Key, "write"}, ":"), writeWaitChanKey: strings.Join([]string{conf.ChanPrefix, conf.Key, "write"}, ":"),
readWaitChanKey: strings.Join([]string{conf.ChanPrefix, conf.Key, "read"}, ":"), readWaitChanKey: strings.Join([]string{conf.ChanPrefix, conf.Key, "read"}, ":"),
rwTokenTimeoutPrefix: conf.TimeoutPrefix, RWTokenTimeoutPrefix: conf.TimeoutPrefix,
} }
return rwLocker return rwLocker
} }

View File

@ -1,4 +1,4 @@
package distributedlock package distributedlock_test
import ( import (
"context" "context"
@ -6,6 +6,7 @@ import (
"testing" "testing"
"time" "time"
dl "modelRT/distributedlock"
"modelRT/distributedlock/constant" "modelRT/distributedlock/constant"
"github.com/redis/go-redis/v9" "github.com/redis/go-redis/v9"
@ -39,28 +40,28 @@ func init() {
func TestRWLockRLockAndUnRLock(t *testing.T) { func TestRWLockRLockAndUnRLock(t *testing.T) {
ctx := context.TODO() ctx := context.TODO()
rwLocker := GetRWLocker(rdb, &RedissionLockConfig{ rwLocker := dl.GetRWLocker(rdb, &dl.RedissionLockConfig{
LockLeaseTime: 120, LockLeaseTime: 120,
NeedRefresh: true, NeedRefresh: true,
Key: "component", Key: "component",
Token: "fd348a84-e07c-4a61-8c19-f753e6bc556a", Token: "fd348a84-e07c-4a61-8c19-f753e6bc556a",
}) })
rwLocker.logger = log rwLocker.Logger = log
duration := 10 * time.Second duration := 10 * time.Second
// 第一次加读锁 // 第一次加读锁
err := rwLocker.RLock(ctx, duration) err := rwLocker.RLock(ctx, duration)
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
tokenKey := strings.Join([]string{rwLocker.rwTokenTimeoutPrefix, rwLocker.token}, ":") tokenKey := strings.Join([]string{rwLocker.RWTokenTimeoutPrefix, rwLocker.Token}, ":")
num, err := rdb.HGet(ctx, rwLocker.key, tokenKey).Int() num, err := rdb.HGet(ctx, rwLocker.Key, tokenKey).Int()
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
assert.Equal(t, 1, num) assert.Equal(t, 1, num)
err = rwLocker.UnRLock(ctx) err = rwLocker.UnRLock(ctx)
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
num, err = rdb.HGet(ctx, rwLocker.key, tokenKey).Int() num, err = rdb.HGet(ctx, rwLocker.Key, tokenKey).Int()
assert.Equal(t, redis.Nil, err) assert.Equal(t, redis.Nil, err)
assert.Equal(t, 0, num) assert.Equal(t, 0, num)
t.Log("rwLock rlock and unrlock test success") t.Log("rwLock rlock and unrlock test success")
@ -70,21 +71,21 @@ func TestRWLockRLockAndUnRLock(t *testing.T) {
func TestRWLockReentrantRLock(t *testing.T) { func TestRWLockReentrantRLock(t *testing.T) {
ctx := context.TODO() ctx := context.TODO()
rwLocker := GetRWLocker(rdb, &RedissionLockConfig{ rwLocker := dl.GetRWLocker(rdb, &dl.RedissionLockConfig{
LockLeaseTime: 120, LockLeaseTime: 120,
NeedRefresh: true, NeedRefresh: true,
Key: "component", Key: "component",
Token: "fd348a84-e07c-4a61-8c19-f753e6bc556a", Token: "fd348a84-e07c-4a61-8c19-f753e6bc556a",
}) })
rwLocker.logger = log rwLocker.Logger = log
duration := 10 * time.Second duration := 10 * time.Second
// 第一次加读锁 // 第一次加读锁
err := rwLocker.RLock(ctx, duration) err := rwLocker.RLock(ctx, duration)
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
tokenKey := strings.Join([]string{rwLocker.rwTokenTimeoutPrefix, rwLocker.token}, ":") tokenKey := strings.Join([]string{rwLocker.RWTokenTimeoutPrefix, rwLocker.Token}, ":")
num, err := rdb.HGet(ctx, rwLocker.key, tokenKey).Int() num, err := rdb.HGet(ctx, rwLocker.Key, tokenKey).Int()
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
assert.Equal(t, 1, num) assert.Equal(t, 1, num)
@ -92,7 +93,7 @@ func TestRWLockReentrantRLock(t *testing.T) {
err = rwLocker.RLock(ctx, duration) err = rwLocker.RLock(ctx, duration)
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
num, err = rdb.HGet(ctx, rwLocker.key, tokenKey).Int() num, err = rdb.HGet(ctx, rwLocker.Key, tokenKey).Int()
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
assert.Equal(t, 2, num) assert.Equal(t, 2, num)
@ -100,7 +101,7 @@ func TestRWLockReentrantRLock(t *testing.T) {
err = rwLocker.UnRLock(ctx) err = rwLocker.UnRLock(ctx)
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
num, err = rdb.HGet(ctx, rwLocker.key, tokenKey).Int() num, err = rdb.HGet(ctx, rwLocker.Key, tokenKey).Int()
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
assert.Equal(t, 1, num) assert.Equal(t, 1, num)
@ -108,7 +109,7 @@ func TestRWLockReentrantRLock(t *testing.T) {
err = rwLocker.UnRLock(ctx) err = rwLocker.UnRLock(ctx)
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
num, err = rdb.HGet(ctx, rwLocker.key, tokenKey).Int() num, err = rdb.HGet(ctx, rwLocker.Key, tokenKey).Int()
assert.Equal(t, redis.Nil, err) assert.Equal(t, redis.Nil, err)
assert.Equal(t, 0, num) assert.Equal(t, 0, num)
t.Log("rwLock reentrant lock test success") t.Log("rwLock reentrant lock test success")
@ -118,29 +119,30 @@ func TestRWLockReentrantRLock(t *testing.T) {
func TestRWLockRefreshRLock(t *testing.T) { func TestRWLockRefreshRLock(t *testing.T) {
ctx := context.TODO() ctx := context.TODO()
rwLocker := GetRWLocker(rdb, &RedissionLockConfig{ rwLocker := dl.GetRWLocker(rdb, &dl.RedissionLockConfig{
LockLeaseTime: 10, LockLeaseTime: 10,
NeedRefresh: true, NeedRefresh: true,
Key: "component", Key: "component",
Token: "fd348a84-e07c-4a61-8c19-f753e6bc556a", Token: "fd348a84-e07c-4a61-8c19-f753e6bc556a",
}) })
rwLocker.logger = log rwLocker.Logger = log
duration := 10 * time.Second duration := 10 * time.Second
// 第一次加读锁 // 第一次加读锁
err := rwLocker.RLock(ctx, duration) err := rwLocker.RLock(ctx, duration)
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
tokenKey := strings.Join([]string{rwLocker.rwTokenTimeoutPrefix, rwLocker.token}, ":") tokenKey := strings.Join([]string{rwLocker.RWTokenTimeoutPrefix, rwLocker.Token}, ":")
num, err := rdb.HGet(ctx, rwLocker.key, tokenKey).Int() num, err := rdb.HGet(ctx, rwLocker.Key, tokenKey).Int()
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
assert.Equal(t, 1, num) assert.Equal(t, 1, num)
time.Sleep(10 * time.Second) time.Sleep(10 * time.Second)
script := `return redis.call('httl', KEYS[1], 'fields', '1', ARGV[1]);` script := `return redis.call('httl', KEYS[1], 'fields', '1', ARGV[1]);`
result, err := rdb.Eval(ctx, script, []string{rwLocker.key}, tokenKey).Result() result, err := rdb.Eval(ctx, script, []string{rwLocker.Key}, tokenKey).Result()
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
ttls, ok := result.([]interface{}) // ttls, ok := result.([]interface{})
ttls, ok := result.([]any)
assert.Equal(t, true, ok) assert.Equal(t, true, ok)
ttl, ok := ttls[0].(int64) ttl, ok := ttls[0].(int64)
assert.Equal(t, true, ok) assert.Equal(t, true, ok)
@ -150,7 +152,7 @@ func TestRWLockRefreshRLock(t *testing.T) {
err = rwLocker.UnRLock(ctx) err = rwLocker.UnRLock(ctx)
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
num, err = rdb.HGet(ctx, rwLocker.key, tokenKey).Int() num, err = rdb.HGet(ctx, rwLocker.Key, tokenKey).Int()
assert.Equal(t, redis.Nil, err) assert.Equal(t, redis.Nil, err)
assert.Equal(t, 0, num) assert.Equal(t, 0, num)
t.Log("rwLock refresh lock test success") t.Log("rwLock refresh lock test success")
@ -160,29 +162,29 @@ func TestRWLockRefreshRLock(t *testing.T) {
func TestRWLock2ClientRLock(t *testing.T) { func TestRWLock2ClientRLock(t *testing.T) {
ctx := context.TODO() ctx := context.TODO()
rwLocker1 := GetRWLocker(rdb, &RedissionLockConfig{ rwLocker1 := dl.GetRWLocker(rdb, &dl.RedissionLockConfig{
LockLeaseTime: 120, LockLeaseTime: 120,
NeedRefresh: true, NeedRefresh: true,
Key: "component", Key: "component",
Token: "fd348a84-e07c-4a61-8c19-f753e6bc556a", Token: "fd348a84-e07c-4a61-8c19-f753e6bc556a",
}) })
rwLocker1.logger = log rwLocker1.Logger = log
rwLocker2 := GetRWLocker(rdb, &RedissionLockConfig{ rwLocker2 := dl.GetRWLocker(rdb, &dl.RedissionLockConfig{
LockLeaseTime: 120, LockLeaseTime: 120,
NeedRefresh: true, NeedRefresh: true,
Key: "component", Key: "component",
Token: "fd348a84-e07c-4a61-8c19-f753e6bc5577", Token: "fd348a84-e07c-4a61-8c19-f753e6bc5577",
}) })
rwLocker2.logger = log rwLocker2.Logger = log
duration := 10 * time.Second duration := 10 * time.Second
// locker1加读锁 // locker1加读锁
err := rwLocker1.RLock(ctx, duration) err := rwLocker1.RLock(ctx, duration)
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
tokenKey1 := strings.Join([]string{rwLocker1.rwTokenTimeoutPrefix, rwLocker1.token}, ":") tokenKey1 := strings.Join([]string{rwLocker1.RWTokenTimeoutPrefix, rwLocker1.Token}, ":")
num, err := rdb.HGet(ctx, rwLocker1.key, tokenKey1).Int() num, err := rdb.HGet(ctx, rwLocker1.Key, tokenKey1).Int()
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
assert.Equal(t, 1, num) assert.Equal(t, 1, num)
@ -190,14 +192,14 @@ func TestRWLock2ClientRLock(t *testing.T) {
err = rwLocker2.RLock(ctx, duration) err = rwLocker2.RLock(ctx, duration)
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
tokenKey2 := strings.Join([]string{rwLocker2.rwTokenTimeoutPrefix, rwLocker2.token}, ":") tokenKey2 := strings.Join([]string{rwLocker2.RWTokenTimeoutPrefix, rwLocker2.Token}, ":")
num, err = rdb.HGet(ctx, rwLocker2.key, tokenKey2).Int() num, err = rdb.HGet(ctx, rwLocker2.Key, tokenKey2).Int()
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
assert.Equal(t, 1, num) assert.Equal(t, 1, num)
err = rdb.HLen(ctx, rwLocker1.key).Err() err = rdb.HLen(ctx, rwLocker1.Key).Err()
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
hLen := rdb.HLen(ctx, rwLocker1.key).Val() hLen := rdb.HLen(ctx, rwLocker1.Key).Val()
assert.Equal(t, int64(3), hLen) assert.Equal(t, int64(3), hLen)
// locker1解读锁 // locker1解读锁
@ -208,9 +210,9 @@ func TestRWLock2ClientRLock(t *testing.T) {
err = rwLocker2.UnRLock(ctx) err = rwLocker2.UnRLock(ctx)
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
err = rdb.Exists(ctx, rwLocker1.key).Err() err = rdb.Exists(ctx, rwLocker1.Key).Err()
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
existNum := rdb.Exists(ctx, rwLocker1.key).Val() existNum := rdb.Exists(ctx, rwLocker1.Key).Val()
assert.Equal(t, int64(0), existNum) assert.Equal(t, int64(0), existNum)
t.Log("rwLock 2 client lock test success") t.Log("rwLock 2 client lock test success")
return return
@ -219,29 +221,29 @@ func TestRWLock2ClientRLock(t *testing.T) {
func TestRWLock2CWith2DifTimeRLock(t *testing.T) { func TestRWLock2CWith2DifTimeRLock(t *testing.T) {
ctx := context.TODO() ctx := context.TODO()
rwLocker1 := GetRWLocker(rdb, &RedissionLockConfig{ rwLocker1 := dl.GetRWLocker(rdb, &dl.RedissionLockConfig{
LockLeaseTime: 120, LockLeaseTime: 120,
NeedRefresh: true, NeedRefresh: true,
Key: "component", Key: "component",
Token: "fd348a84-e07c-4a61-8c19-f753e6bc556a", Token: "fd348a84-e07c-4a61-8c19-f753e6bc556a",
}) })
rwLocker1.logger = log rwLocker1.Logger = log
rwLocker2 := GetRWLocker(rdb, &RedissionLockConfig{ rwLocker2 := dl.GetRWLocker(rdb, &dl.RedissionLockConfig{
LockLeaseTime: 30, LockLeaseTime: 30,
NeedRefresh: true, NeedRefresh: true,
Key: "component", Key: "component",
Token: "fd348a84-e07c-4a61-8c19-f753e6bc5577", Token: "fd348a84-e07c-4a61-8c19-f753e6bc5577",
}) })
rwLocker2.logger = log rwLocker2.Logger = log
duration := 10 * time.Second duration := 10 * time.Second
// locker1加读锁 // locker1加读锁
err := rwLocker1.RLock(ctx, duration) err := rwLocker1.RLock(ctx, duration)
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
tokenKey1 := strings.Join([]string{rwLocker1.rwTokenTimeoutPrefix, rwLocker1.token}, ":") tokenKey1 := strings.Join([]string{rwLocker1.RWTokenTimeoutPrefix, rwLocker1.Token}, ":")
num, err := rdb.HGet(ctx, rwLocker1.key, tokenKey1).Int() num, err := rdb.HGet(ctx, rwLocker1.Key, tokenKey1).Int()
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
assert.Equal(t, 1, num) assert.Equal(t, 1, num)
@ -249,20 +251,21 @@ func TestRWLock2CWith2DifTimeRLock(t *testing.T) {
err = rwLocker2.RLock(ctx, duration) err = rwLocker2.RLock(ctx, duration)
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
tokenKey2 := strings.Join([]string{rwLocker2.rwTokenTimeoutPrefix, rwLocker2.token}, ":") tokenKey2 := strings.Join([]string{rwLocker2.RWTokenTimeoutPrefix, rwLocker2.Token}, ":")
num, err = rdb.HGet(ctx, rwLocker2.key, tokenKey2).Int() num, err = rdb.HGet(ctx, rwLocker2.Key, tokenKey2).Int()
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
assert.Equal(t, 1, num) assert.Equal(t, 1, num)
err = rdb.HLen(ctx, rwLocker1.key).Err() err = rdb.HLen(ctx, rwLocker1.Key).Err()
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
hLen := rdb.HLen(ctx, rwLocker1.key).Val() hLen := rdb.HLen(ctx, rwLocker1.Key).Val()
assert.Equal(t, int64(3), hLen) assert.Equal(t, int64(3), hLen)
script := `return redis.call('httl', KEYS[1], 'fields', '1', ARGV[1]);` script := `return redis.call('httl', KEYS[1], 'fields', '1', ARGV[1]);`
result, err := rdb.Eval(ctx, script, []string{rwLocker1.key}, tokenKey1).Result() result, err := rdb.Eval(ctx, script, []string{rwLocker1.Key}, tokenKey1).Result()
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
ttls, ok := result.([]interface{}) // ttls, ok := result.([]interface{})
ttls, ok := result.([]any)
assert.Equal(t, true, ok) assert.Equal(t, true, ok)
ttl, ok := ttls[0].(int64) ttl, ok := ttls[0].(int64)
assert.Equal(t, true, ok) assert.Equal(t, true, ok)
@ -273,16 +276,16 @@ func TestRWLock2CWith2DifTimeRLock(t *testing.T) {
err = rwLocker1.UnRLock(ctx) err = rwLocker1.UnRLock(ctx)
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
hashTTL := rdb.TTL(ctx, rwLocker1.key).Val().Seconds() hashTTL := rdb.TTL(ctx, rwLocker1.Key).Val().Seconds()
assert.Greater(t, hashTTL, float64(20)) assert.Greater(t, hashTTL, float64(20))
// locker2解读锁 // locker2解读锁
err = rwLocker2.UnRLock(ctx) err = rwLocker2.UnRLock(ctx)
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
err = rdb.Exists(ctx, rwLocker1.key).Err() err = rdb.Exists(ctx, rwLocker1.Key).Err()
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
existNum := rdb.Exists(ctx, rwLocker1.key).Val() existNum := rdb.Exists(ctx, rwLocker1.Key).Val()
assert.Equal(t, int64(0), existNum) assert.Equal(t, int64(0), existNum)
t.Log("rwLock 2 client lock test success") t.Log("rwLock 2 client lock test success")
return return
@ -291,29 +294,29 @@ func TestRWLock2CWith2DifTimeRLock(t *testing.T) {
func TestRWLock2CWithTimeTransformRLock(t *testing.T) { func TestRWLock2CWithTimeTransformRLock(t *testing.T) {
ctx := context.TODO() ctx := context.TODO()
rwLocker1 := GetRWLocker(rdb, &RedissionLockConfig{ rwLocker1 := dl.GetRWLocker(rdb, &dl.RedissionLockConfig{
LockLeaseTime: 30, LockLeaseTime: 30,
NeedRefresh: true, NeedRefresh: true,
Key: "component", Key: "component",
Token: "fd348a84-e07c-4a61-8c19-f753e6bc556a", Token: "fd348a84-e07c-4a61-8c19-f753e6bc556a",
}) })
rwLocker1.logger = log rwLocker1.Logger = log
rwLocker2 := GetRWLocker(rdb, &RedissionLockConfig{ rwLocker2 := dl.GetRWLocker(rdb, &dl.RedissionLockConfig{
LockLeaseTime: 120, LockLeaseTime: 120,
NeedRefresh: true, NeedRefresh: true,
Key: "component", Key: "component",
Token: "fd348a84-e07c-4a61-8c19-f753e6bc5577", Token: "fd348a84-e07c-4a61-8c19-f753e6bc5577",
}) })
rwLocker2.logger = log rwLocker2.Logger = log
duration := 10 * time.Second duration := 10 * time.Second
// locker1加读锁 // locker1加读锁
err := rwLocker1.RLock(ctx, duration) err := rwLocker1.RLock(ctx, duration)
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
tokenKey1 := strings.Join([]string{rwLocker1.rwTokenTimeoutPrefix, rwLocker1.token}, ":") tokenKey1 := strings.Join([]string{rwLocker1.RWTokenTimeoutPrefix, rwLocker1.Token}, ":")
num, err := rdb.HGet(ctx, rwLocker1.key, tokenKey1).Int() num, err := rdb.HGet(ctx, rwLocker1.Key, tokenKey1).Int()
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
assert.Equal(t, 1, num) assert.Equal(t, 1, num)
@ -321,17 +324,17 @@ func TestRWLock2CWithTimeTransformRLock(t *testing.T) {
err = rwLocker2.RLock(ctx, duration) err = rwLocker2.RLock(ctx, duration)
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
tokenKey2 := strings.Join([]string{rwLocker2.rwTokenTimeoutPrefix, rwLocker2.token}, ":") tokenKey2 := strings.Join([]string{rwLocker2.RWTokenTimeoutPrefix, rwLocker2.Token}, ":")
num, err = rdb.HGet(ctx, rwLocker2.key, tokenKey2).Int() num, err = rdb.HGet(ctx, rwLocker2.Key, tokenKey2).Int()
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
assert.Equal(t, 1, num) assert.Equal(t, 1, num)
err = rdb.HLen(ctx, rwLocker1.key).Err() err = rdb.HLen(ctx, rwLocker1.Key).Err()
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
hLen := rdb.HLen(ctx, rwLocker1.key).Val() hLen := rdb.HLen(ctx, rwLocker1.Key).Val()
assert.Equal(t, int64(3), hLen) assert.Equal(t, int64(3), hLen)
hashTTL := rdb.TTL(ctx, rwLocker2.key).Val().Seconds() hashTTL := rdb.TTL(ctx, rwLocker2.Key).Val().Seconds()
assert.Greater(t, hashTTL, float64(100)) assert.Greater(t, hashTTL, float64(100))
// locker2解读锁 // locker2解读锁
@ -339,16 +342,16 @@ func TestRWLock2CWithTimeTransformRLock(t *testing.T) {
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
time.Sleep(10 * time.Second) time.Sleep(10 * time.Second)
hashTTL = rdb.TTL(ctx, rwLocker1.key).Val().Seconds() hashTTL = rdb.TTL(ctx, rwLocker1.Key).Val().Seconds()
assert.Greater(t, hashTTL, float64(15)) assert.Greater(t, hashTTL, float64(15))
// locker1解读锁 // locker1解读锁
err = rwLocker1.UnRLock(ctx) err = rwLocker1.UnRLock(ctx)
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
err = rdb.Exists(ctx, rwLocker1.key).Err() err = rdb.Exists(ctx, rwLocker1.Key).Err()
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
existNum := rdb.Exists(ctx, rwLocker1.key).Val() existNum := rdb.Exists(ctx, rwLocker1.Key).Val()
assert.Equal(t, int64(0), existNum) assert.Equal(t, int64(0), existNum)
t.Log("rwLock 2 client lock test success") t.Log("rwLock 2 client lock test success")
return return
@ -357,28 +360,28 @@ func TestRWLock2CWithTimeTransformRLock(t *testing.T) {
func TestRWLockWLockAndUnWLock(t *testing.T) { func TestRWLockWLockAndUnWLock(t *testing.T) {
ctx := context.TODO() ctx := context.TODO()
rwLocker := GetRWLocker(rdb, &RedissionLockConfig{ rwLocker := dl.GetRWLocker(rdb, &dl.RedissionLockConfig{
LockLeaseTime: 120, LockLeaseTime: 120,
NeedRefresh: true, NeedRefresh: true,
Key: "component", Key: "component",
Token: "fd348a84-e07c-4a61-8c19-f753e6bc556a", Token: "fd348a84-e07c-4a61-8c19-f753e6bc556a",
}) })
rwLocker.logger = log rwLocker.Logger = log
duration := 10 * time.Second duration := 10 * time.Second
// 第一次加读锁 // 第一次加读锁
err := rwLocker.WLock(ctx, duration) err := rwLocker.WLock(ctx, duration)
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
tokenKey := strings.Join([]string{rwLocker.rwTokenTimeoutPrefix, rwLocker.token}, ":") tokenKey := strings.Join([]string{rwLocker.RWTokenTimeoutPrefix, rwLocker.Token}, ":")
num, err := rdb.HGet(ctx, rwLocker.key, tokenKey).Int() num, err := rdb.HGet(ctx, rwLocker.Key, tokenKey).Int()
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
assert.Equal(t, 1, num) assert.Equal(t, 1, num)
err = rwLocker.UnWLock(ctx) err = rwLocker.UnWLock(ctx)
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
num, err = rdb.HGet(ctx, rwLocker.key, tokenKey).Int() num, err = rdb.HGet(ctx, rwLocker.Key, tokenKey).Int()
assert.Equal(t, redis.Nil, err) assert.Equal(t, redis.Nil, err)
assert.Equal(t, 0, num) assert.Equal(t, 0, num)
t.Log("rwLock rlock and unrlock test success") t.Log("rwLock rlock and unrlock test success")
@ -388,21 +391,21 @@ func TestRWLockWLockAndUnWLock(t *testing.T) {
func TestRWLockReentrantWLock(t *testing.T) { func TestRWLockReentrantWLock(t *testing.T) {
ctx := context.TODO() ctx := context.TODO()
rwLocker := GetRWLocker(rdb, &RedissionLockConfig{ rwLocker := dl.GetRWLocker(rdb, &dl.RedissionLockConfig{
LockLeaseTime: 120, LockLeaseTime: 120,
NeedRefresh: true, NeedRefresh: true,
Key: "component", Key: "component",
Token: "fd348a84-e07c-4a61-8c19-f753e6bc556a", Token: "fd348a84-e07c-4a61-8c19-f753e6bc556a",
}) })
rwLocker.logger = log rwLocker.Logger = log
duration := 10 * time.Second duration := 10 * time.Second
// 第一次加写锁 // 第一次加写锁
err := rwLocker.WLock(ctx, duration) err := rwLocker.WLock(ctx, duration)
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
tokenKey := strings.Join([]string{rwLocker.rwTokenTimeoutPrefix, rwLocker.token}, ":") tokenKey := strings.Join([]string{rwLocker.RWTokenTimeoutPrefix, rwLocker.Token}, ":")
num, err := rdb.HGet(ctx, rwLocker.key, tokenKey).Int() num, err := rdb.HGet(ctx, rwLocker.Key, tokenKey).Int()
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
assert.Equal(t, 1, num) assert.Equal(t, 1, num)
@ -410,7 +413,7 @@ func TestRWLockReentrantWLock(t *testing.T) {
err = rwLocker.WLock(ctx, duration) err = rwLocker.WLock(ctx, duration)
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
num, err = rdb.HGet(ctx, rwLocker.key, tokenKey).Int() num, err = rdb.HGet(ctx, rwLocker.Key, tokenKey).Int()
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
assert.Equal(t, 2, num) assert.Equal(t, 2, num)
@ -418,7 +421,7 @@ func TestRWLockReentrantWLock(t *testing.T) {
err = rwLocker.UnWLock(ctx) err = rwLocker.UnWLock(ctx)
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
num, err = rdb.HGet(ctx, rwLocker.key, tokenKey).Int() num, err = rdb.HGet(ctx, rwLocker.Key, tokenKey).Int()
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
assert.Equal(t, 1, num) assert.Equal(t, 1, num)
@ -426,7 +429,7 @@ func TestRWLockReentrantWLock(t *testing.T) {
err = rwLocker.UnWLock(ctx) err = rwLocker.UnWLock(ctx)
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
num, err = rdb.HGet(ctx, rwLocker.key, tokenKey).Int() num, err = rdb.HGet(ctx, rwLocker.Key, tokenKey).Int()
assert.Equal(t, redis.Nil, err) assert.Equal(t, redis.Nil, err)
assert.Equal(t, 0, num) assert.Equal(t, 0, num)
t.Log("rwLock reentrant lock test success") t.Log("rwLock reentrant lock test success")
@ -436,29 +439,29 @@ func TestRWLockReentrantWLock(t *testing.T) {
func TestRWLock2CWithRLockAndWLockFailed(t *testing.T) { func TestRWLock2CWithRLockAndWLockFailed(t *testing.T) {
ctx := context.TODO() ctx := context.TODO()
rwLocker1 := GetRWLocker(rdb, &RedissionLockConfig{ rwLocker1 := dl.GetRWLocker(rdb, &dl.RedissionLockConfig{
LockLeaseTime: 120, LockLeaseTime: 120,
NeedRefresh: true, NeedRefresh: true,
Key: "component", Key: "component",
Token: "fd348a84-e07c-4a61-8c19-f753e6bc556a", Token: "fd348a84-e07c-4a61-8c19-f753e6bc556a",
}) })
rwLocker1.logger = log rwLocker1.Logger = log
rwLocker2 := GetRWLocker(rdb, &RedissionLockConfig{ rwLocker2 := dl.GetRWLocker(rdb, &dl.RedissionLockConfig{
LockLeaseTime: 30, LockLeaseTime: 30,
NeedRefresh: true, NeedRefresh: true,
Key: "component", Key: "component",
Token: "fd348a84-e07c-4a61-8c19-f753e6bc5577", Token: "fd348a84-e07c-4a61-8c19-f753e6bc5577",
}) })
rwLocker2.logger = log rwLocker2.Logger = log
duration := 10 * time.Second duration := 10 * time.Second
// locker1加读锁 // locker1加读锁
err := rwLocker1.RLock(ctx, duration) err := rwLocker1.RLock(ctx, duration)
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
tokenKey1 := strings.Join([]string{rwLocker1.rwTokenTimeoutPrefix, rwLocker1.token}, ":") tokenKey1 := strings.Join([]string{rwLocker1.RWTokenTimeoutPrefix, rwLocker1.Token}, ":")
num, err := rdb.HGet(ctx, rwLocker1.key, tokenKey1).Int() num, err := rdb.HGet(ctx, rwLocker1.Key, tokenKey1).Int()
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
assert.Equal(t, 1, num) assert.Equal(t, 1, num)
@ -476,28 +479,28 @@ func TestRWLock2CWithRLockAndWLockFailed(t *testing.T) {
func TestRWLock2CWithRLockAndWLockSucceed(t *testing.T) { func TestRWLock2CWithRLockAndWLockSucceed(t *testing.T) {
ctx := context.TODO() ctx := context.TODO()
rwLocker1 := GetRWLocker(rdb, &RedissionLockConfig{ rwLocker1 := dl.GetRWLocker(rdb, &dl.RedissionLockConfig{
LockLeaseTime: 120, LockLeaseTime: 120,
NeedRefresh: true, NeedRefresh: true,
Key: "component", Key: "component",
Token: "fd348a84-e07c-4a61-8c19-f753e6bc556a", Token: "fd348a84-e07c-4a61-8c19-f753e6bc556a",
}) })
rwLocker1.logger = log rwLocker1.Logger = log
rwLocker2 := GetRWLocker(rdb, &RedissionLockConfig{ rwLocker2 := dl.GetRWLocker(rdb, &dl.RedissionLockConfig{
LockLeaseTime: 120, LockLeaseTime: 120,
NeedRefresh: true, NeedRefresh: true,
Key: "component", Key: "component",
Token: "fd348a84-e07c-4a61-8c19-f753e6bc5577", Token: "fd348a84-e07c-4a61-8c19-f753e6bc5577",
}) })
rwLocker2.logger = log rwLocker2.Logger = log
duration := 10 * time.Second duration := 10 * time.Second
// locker1加读锁 // locker1加读锁
err := rwLocker1.RLock(ctx, duration) err := rwLocker1.RLock(ctx, duration)
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
tokenKey1 := strings.Join([]string{rwLocker1.rwTokenTimeoutPrefix, rwLocker1.token}, ":") tokenKey1 := strings.Join([]string{rwLocker1.RWTokenTimeoutPrefix, rwLocker1.Token}, ":")
num, err := rdb.HGet(ctx, rwLocker1.key, tokenKey1).Int() num, err := rdb.HGet(ctx, rwLocker1.Key, tokenKey1).Int()
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
assert.Equal(t, 1, num) assert.Equal(t, 1, num)
@ -513,8 +516,8 @@ func TestRWLock2CWithRLockAndWLockSucceed(t *testing.T) {
err = rwLocker2.WLock(ctx, duration) err = rwLocker2.WLock(ctx, duration)
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
tokenKey2 := strings.Join([]string{rwLocker2.rwTokenTimeoutPrefix, rwLocker2.token}, ":") tokenKey2 := strings.Join([]string{rwLocker2.RWTokenTimeoutPrefix, rwLocker2.Token}, ":")
num, err = rdb.HGet(ctx, rwLocker2.key, tokenKey2).Int() num, err = rdb.HGet(ctx, rwLocker2.Key, tokenKey2).Int()
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
assert.Equal(t, 1, num) assert.Equal(t, 1, num)
@ -529,29 +532,29 @@ func TestRWLock2CWithRLockAndWLockSucceed(t *testing.T) {
func TestRWLock2CWithWLockAndRLock(t *testing.T) { func TestRWLock2CWithWLockAndRLock(t *testing.T) {
ctx := context.TODO() ctx := context.TODO()
rwLocker1 := GetRWLocker(rdb, &RedissionLockConfig{ rwLocker1 := dl.GetRWLocker(rdb, &dl.RedissionLockConfig{
LockLeaseTime: 120, LockLeaseTime: 120,
NeedRefresh: true, NeedRefresh: true,
Key: "component", Key: "component",
Token: "fd348a84-e07c-4a61-8c19-f753e6bc556a", Token: "fd348a84-e07c-4a61-8c19-f753e6bc556a",
}) })
rwLocker1.logger = log rwLocker1.Logger = log
rwLocker2 := GetRWLocker(rdb, &RedissionLockConfig{ rwLocker2 := dl.GetRWLocker(rdb, &dl.RedissionLockConfig{
LockLeaseTime: 30, LockLeaseTime: 30,
NeedRefresh: true, NeedRefresh: true,
Key: "component", Key: "component",
Token: "fd348a84-e07c-4a61-8c19-f753e6bc5577", Token: "fd348a84-e07c-4a61-8c19-f753e6bc5577",
}) })
rwLocker2.logger = log rwLocker2.Logger = log
duration := 10 * time.Second duration := 10 * time.Second
// locker1加写锁 // locker1加写锁
err := rwLocker1.WLock(ctx, duration) err := rwLocker1.WLock(ctx, duration)
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
tokenKey1 := strings.Join([]string{rwLocker1.rwTokenTimeoutPrefix, rwLocker1.token}, ":") tokenKey1 := strings.Join([]string{rwLocker1.RWTokenTimeoutPrefix, rwLocker1.Token}, ":")
num, err := rdb.HGet(ctx, rwLocker1.key, tokenKey1).Int() num, err := rdb.HGet(ctx, rwLocker1.Key, tokenKey1).Int()
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
assert.Equal(t, 1, num) assert.Equal(t, 1, num)
@ -567,8 +570,8 @@ func TestRWLock2CWithWLockAndRLock(t *testing.T) {
err = rwLocker2.RLock(ctx, duration) err = rwLocker2.RLock(ctx, duration)
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
tokenKey2 := strings.Join([]string{rwLocker2.rwTokenTimeoutPrefix, rwLocker2.token}, ":") tokenKey2 := strings.Join([]string{rwLocker2.RWTokenTimeoutPrefix, rwLocker2.Token}, ":")
num, err = rdb.HGet(ctx, rwLocker2.key, tokenKey2).Int() num, err = rdb.HGet(ctx, rwLocker2.Key, tokenKey2).Int()
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
assert.Equal(t, 1, num) assert.Equal(t, 1, num)