redlock

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Jan 11, 2021 License: BSD-3-Clause Imports: 7 Imported by: 0

README

RedLock

根据 https://redis.io/topics/distlock 实现的分布式锁

没有实现多redis节点的增强版本,仅仅实现了单redis节点的方式

API

创建锁

import (
	"github.com/go-redis/redis"
	"github.com/joyparty/redlock"
)

name := "..." // 锁名字,每把锁应该有唯一的名字,名字相同的锁会互斥
ttl := 10 * time.Second	// 锁记录过期时间

var client redis.Cmdable
// 模块配置,指定默认redis客户端
redlock.SetDefaultClient(client)
// 使用默认redis客户端创建锁
mux, err := redlock.NewMutex(name, ttl)

// 使用自己指定的redis客户端创建锁
mux, err := redlock.NewMutexFromClient(name, ttl, client)

锁定

if err := mux.Lock(context.TODO()); err == redlock.ErrLockConflict {
	fmt.Println("已经被锁定了")
} else if err != nil {
	fmt.Println(err)
}

解锁

if err := mux.Unlock(context.TODO()); err == redlock.ErrLockExpired {
	fmt.Println("锁记录过期或不存在")
} else if err != nil {
	fmt.Println(err)
}

延长锁的过期时间,延长的时间使用创建锁的ttl参数值

if err := mux.Extend(context.TODO()); err == redlock.ErrLockExpired {
	fmt.Println("锁记录过期或不存在")
} else if err != nil {
	fmt.Println(err)
}

锁定并执行

var task func(ctx context.Context) error

if err := mux.Do(context.TODO(), task).Err(); err != nil {
	if err == redlock.ErrLockConflict {
		fmt.Println("锁定失败")
	} else if err == redlock.ErrLockExpired {
		fmt.Println("锁记录过期或不存在")
	} else {
		fmt.Println(err)
	}
}

使用说明

TTL参数

TTL参数设置短了,有可能任务还没有执行完毕,锁记录就过期了。但是设置过长的值,任务异常退出没有正确的释放锁,导致其它的实例也被锁定在外面。

所以,TTL的取值会基于两方面的考虑,任务正常情况下执行的最长时间,假设锁没有得到正常的释放,希望在多短的时间内其它的实例能够接替继续执行,TTL应该在这两个时间之间取值。

延时

实际使用中,有时候很难确定一个任务会跑多长时间,预期是一回事,实际是另外一回事。这就导致预估的TTL值实际上不好用。

应对的方式就是在任务执行过程中定期给锁记录延长过期时间,只要任务还在执行就会一直延长,直到任务执行完毕后释放锁。

锁定并执行

任务边执行边延长锁记录是一个看起来简单但烦琐的事情,需要处理以下情况:

  • 任务执行过程中,周期性的延时
  • 任务执行完毕后,中断延时并释放锁
  • 延时失败时,中断任务执行
  • 外部传递进来的context cancel时,中断延时和任务

因此redlock提供了Do方法封装了这部分复杂性,调用方只需要编写自己的业务逻辑即可。

Do的延时周期是ttl参数的一半,假设设置的ttl为10秒,每5秒就会给锁记录延时10秒。

任务的中断是由context包来实现的,调用方有责任处理传递进来的ctx cancel行为,否则有可能导致锁已经异常过期了,任务还在继续执行的情况。

Documentation

Overview

Package redlock 是根据 https://redis.io/topics/distlock 实现的分布式锁

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrLockConflict 锁定冲突
	ErrLockConflict = fmt.Errorf("lock conflict")
	// ErrLockExpired 锁已过期不存在
	ErrLockExpired = fmt.Errorf("lock not exist or expired")
)

Functions

func SetDefaultClient

func SetDefaultClient(c redis.Cmdable)

SetDefaultClient 设置默认redis客户端

Types

type Mutex

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

Mutex 锁

func NewMutex

func NewMutex(name string, ttl time.Duration) (*Mutex, error)

NewMutex 新建锁对象

func NewMutexFromClient

func NewMutexFromClient(name string, ttl time.Duration, c redis.Cmdable) (*Mutex, error)

NewMutexFromClient 使用redis客户端创建锁

func (*Mutex) Do

func (mux *Mutex) Do(ctx context.Context, task func(ctx context.Context) error) (result Result)

Do 锁定后执行

func (*Mutex) Extend

func (mux *Mutex) Extend(ctx context.Context) error

Extend 延长锁过期时间,继续持有

func (*Mutex) Lock

func (mux *Mutex) Lock(ctx context.Context) error

Lock 锁定,失败不会重试

func (*Mutex) Unlock

func (mux *Mutex) Unlock(ctx context.Context) error

Unlock 解除锁定

type Result

type Result struct {
	LockErr error
	TaskErr error
}

Result Mutext.Do()返回值类型

func (Result) Err

func (r Result) Err() error

Err 获取错误

Jump to

Keyboard shortcuts

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