rlock

package module
v0.0.3 Latest Latest
Warning

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

Go to latest
Published: Nov 9, 2023 License: Apache-2.0 Imports: 9 Imported by: 14

README

redis-lock

基于 Redis 实现的分布式锁。要求:

  • Redis >= v7: 理论上来说低于 Redis 7 的也可以,但是暂时我还没在这些 Redis 版本上测试
  • Go >= 18: 低版本的 Go 应该也可以,只要能够编译通过就可以

关于极客时间课程

demo 文件夹的内容是我在极客时间讲如何实现一个分布式锁的时候现场写下的代码,虽然里面有 BUG,但是我还是保留了下来。

真正可用的代码就在这个目录下,demo 中的只是用来辅助学习极客时间的。

加入我们

Documentation

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	ErrFailedToPreemptLock = errors.New("rlock: 抢锁失败")
	// ErrLockNotHold 一般是出现在你预期你本来持有锁,结果却没有持有锁的地方
	// 比如说当你尝试释放锁的时候,可能得到这个错误
	// 这一般意味着有人绕开了 rlock 的控制,直接操作了 Redis
	ErrLockNotHold = errors.New("rlock: 未持有锁")
)

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, retry RetryStrategy, timeout time.Duration) (*Lock, error)

Lock 是尽可能重试减少加锁失败的可能 Lock 会在超时或者锁正被人持有的时候进行重试 最后返回的 error 使用 errors.Is 判断,可能是: - context.DeadlineExceeded: Lock 整体调用超时 - ErrFailedToPreemptLock: 超过重试次数,但是整个重试过程都没有出现错误 - DeadlineExceeded 和 ErrFailedToPreemptLock: 超过重试次数,但是最后一次重试超时了 你在使用的过程中,应该注意: - 如果 errors.Is(err, context.DeadlineExceeded) 那么最终有没有加锁成功,谁也不知道 - 如果 errors.Is(err, ErrFailedToPreemptLock) 说明肯定没成功,而且超过了重试次数 - 否则,和 Redis 通信出了问题

func (*Client) SingleflightLock

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

func (*Client) TryLock

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

type FixIntervalRetry

type FixIntervalRetry struct {
	// 重试间隔
	Interval time.Duration
	// 最大次数
	Max int
	// contains filtered or unexported fields
}

func (*FixIntervalRetry) Next

func (f *FixIntervalRetry) Next() (time.Duration, bool)

type Lock

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

func (*Lock) AutoRefresh

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

func (*Lock) Refresh

func (l *Lock) Refresh(ctx context.Context) error
Example
var lock *Lock
end := make(chan struct{}, 1)
go func() {
	ticker := time.NewTicker(time.Second * 30)
	for {
		select {
		case <-ticker.C:
			ctx, cancel := context.WithTimeout(context.Background(), time.Second)
			err := lock.Refresh(ctx)
			cancel()
			// 错误处理

			if err == context.DeadlineExceeded {
				// 超时,按照道理来说,你应该立刻重试
				// 超时之下可能续约成功了,也可能没成功
			}
			if err != nil {
				// 其它错误,你要考虑这个错误能不能继续处理
				// 如果不能处理,你怎么通知后续业务中断?
			}
		case <-end:
			// 你的业务退出了
		}
	}
}()
// 后面是你的业务
fmt.Println("Finish")
// 你的业务完成了
end <- struct{}{}
Output:

Finish

func (*Lock) Unlock

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

Unlock 解锁

type RetryStrategy

type RetryStrategy interface {
	// Next 返回下一次重试的间隔,如果不需要继续重试,那么第二参数发挥 false
	Next() (time.Duration, bool)
}

Directories

Path Synopsis
Code generated by MockGen.
Code generated by MockGen.

Jump to

Keyboard shortcuts

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