go中基于redis实现的分布式锁
我们在实际开发中,经常会遇到对共享变量进行多线程同步访问的情况,为了保证数据、业务的一致性,我们需要用到分布式锁进行处理。
import (
"context"
"errors"
"fmt"
"github.com/go-redis/redis/v8"
"sync"
"time"
)
type Locker struct {
prefix string
client *redis.Client
}
var (
instance *Locker
once sync.Once
defaultTimeout = 3 * time.Second
ErrClientIsNil = errors.New("client is nil, stop command")
)
// NewLocker 获取锁实例
func NewLocker(addr, password string, db int) *Locker {
once.Do(func() {
instance = new(Locker)
instance.prefix = "rlc"
instance.client = redis.NewClient(&redis.Options{
Addr: addr,
Password: password,
DB: db,
})
})
return instance
}
// Lock 请求锁资源
// 注意超时设定,如果不带单位,那么默认是纳秒,所以一定要带上单位。如果传入0,那么默认是 `defaultTimeout`
//
func (lock *Locker) Lock(ctx context.Context, key string, value interface{}, expiration time.Duration) (bool, error) {
if lock.client == nil {
return false, ErrClientIsNil
}
if expiration <= 0 {
expiration = defaultTimeout
}
k := lock.key(key)
result, err := lock.client.SetNX(ctx, k, value, expiration).Result()
if err != nil {
return false, err
}
return result, nil
}
// Unlock 主动释放锁资源
func (lock *Locker) Unlock(ctx context.Context, key string) (int64, error) {
if lock.client == nil {
return 0, ErrClientIsNil
}
k := lock.key(key)
val, err := lock.client.Del(ctx, k).Result()
if err != nil {
return 0, err
}
return val, nil
}
// key 加上默认前缀,作为区分
func (lock *Locker) key(key string) string {
return fmt.Sprintf("%s:%s", lock.prefix, key)
}
本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。