redis_lock

package
v0.0.0-...-a36d113 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 17, 2024 License: Apache-2.0 Imports: 7 Imported by: 0

Documentation

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	ErrFailedToPreemptLock = errors.New("failed to preempt lock")
	ErrLockNotHeld         = errors.New("lock not held")
)

Functions

This section is empty.

Types

type Client

type Client struct {
	// contains filtered or unexported fields
}

func NewClient

func NewClient(client redis.Cmdable) *Client

func (*Client) Lock

func (c *Client) Lock(ctx context.Context, key string,
	expiration time.Duration, timeout time.Duration, retry RetryStrategy) (*Lock, error)

func (*Client) TryLock

func (c *Client) TryLock(ctx context.Context,
	key string, expiration time.Duration) (*Lock, error)

type FixedIntervalRetryStrategy

type FixedIntervalRetryStrategy struct {
	Interval time.Duration
	MaxCnt   int
	// contains filtered or unexported fields
}

FixedIntervalRetryStrategy 固定间隔重试

func (*FixedIntervalRetryStrategy) Next

type Lock

type Lock struct {
	// contains filtered or unexported fields
}

func (*Lock) AutoRefresh

func (l *Lock) AutoRefresh(interval time.Duration, timeout time.Duration) error

AutoRefresh 不建议使用这个API,用户最好是自己手动控制Refresh interval 多久续约一次 timeout 调用续约的超时时间

Example
// 抢到了锁
var l *Lock
go func() {
	_ = l.AutoRefresh(time.Millisecond, time.Second*3)
	// 处理错误,需要中断业务

}()
// 执行业务

fmt.Println("Hello")
Output:

Hello

func (*Lock) Refresh

func (l *Lock) Refresh(ctx context.Context) error

Refresh 用户手动续约

Example
// 假设加锁成功,拿到了lock
var l *Lock
// 业务执行完毕的信号
stopChan := make(chan struct{})
// 续约失败的信号
errChan := make(chan error)
done := false
go func() {
	timeoutChan := make(chan struct{}, 1)
	ticker := time.NewTicker(time.Second * 10)
	defer ticker.Stop()
	for !done {
		select {
		case <-stopChan:
			ctx, cancel := context.WithTimeout(context.Background(), time.Second)
			_ = l.Unlock(ctx)
			cancel()
			done = true
		case <-ticker.C:
			ctx, cancel := context.WithTimeout(context.Background(), time.Second)
			err := l.Refresh(ctx)
			cancel()
			switch {
			// 处理error
			case errors.Is(err, context.DeadlineExceeded):
				// 自己给自己发信号,channel一定要带缓存的
				timeoutChan <- struct{}{}
				continue
			case err == nil:
			default:
				errChan <- err
				_ = l.Unlock(ctx)
				done = true
			}
		case <-timeoutChan:
			// 尝试一次重试
			ctx, cancel := context.WithTimeout(context.Background(), time.Second)
			err := l.Refresh(ctx)
			cancel()
			if err == nil {
				// 重试成功
				continue
			}
			errChan <- err
			_ = l.Unlock(ctx)
			done = true
		}
	}
}()
// 你的业务
// 业务方在执行时,要在中间步骤检测errChan有没有信号
// 如果续约失败了,业务方需要中断正在处理的业务

// 如果业务在循环中执行
FOR:
for i := 0; i < 100; i++ {
	select {
	case <-errChan:
		// 中断业务
		break FOR
	default:
		// 正常的业务处理
	}
}

// 如果你的业务是在多个步骤中执行
select {
case <-errChan:
// 中断业务
default:
	// 步骤一
}
select {
case <-errChan:
// 中断业务
default:
	// 步骤二
}

select {
case <-errChan:
// 中断业务
default:
	// 步骤n
}

// 使用context进行传递中断信号
ctx, cancel := context.WithCancel(context.Background())
// 防止正常结束了业务,但是没人发送ctx取消信号而导致goroutine泄露
defer cancel()
go func() {
	for {
		select {
		case <-errChan:
			cancel()
			continue
		case <-ctx.Done():
			// 这一个是为了防止goroutine泄露
			return
		}
	}
}()
// 使用ctx往下传递,下游在执行前需要先判断一下ctx有没有被cancel()
// Next(ctx)

// 业务退出就要退出续约循环
// 不管是被中断了还是正常结束,都需要发送这个信号
stopChan <- struct{}{}
close(stopChan)
close(errChan)

fmt.Println("Hello")
Output:

Hello

func (*Lock) Unlock

func (l *Lock) Unlock(ctx context.Context) error

type RetryStrategy

type RetryStrategy interface {
	// Next
	// 第一个返回值:重试的间隔
	// 第二个返回值:要不要继续重试
	Next() (time.Duration, bool)
}

Directories

Path Synopsis
mock
redis
Package redismock is a generated GoMock package.
Package redismock is a generated GoMock package.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL