Golang 基于单节点 Redis 实现的分布式锁
分布式锁的基本思路
原子性加锁
加锁问题,为避免死锁,需要加锁的时候为锁设置一个存活时间,过了这个时间,锁自动被释放掉
安全释放锁
1.安全释放锁需要达到一个目的,A客户端加锁,必须由A客户端释放锁或者锁超时自动释放
2.锁重入问题,A线程由于业务处理时间过长,比存活时间还要长,锁到期自动释放,就会导致A进程去释放掉其它进程产生的锁
3.为了解决锁重入问题,我们需要在A客户端业务逻辑处理过程中,延长这个锁的存活时间,防止业务未处理完成导致锁自动释放.(租约,自动续期)
代码
package dislock
import (
"context"
"errors"
"github.com/gomodule/redigo/redis"
"log"
"time"
)
//基于redis的分布式锁
type DisLockRedis struct {
key string //锁名称
ttl int64 //锁超时时间
isLocked bool //上锁成功标识
cancelFun context.CancelFunc //用于取消自动续租携程
redis *redis.Pool
debug bool
}
func NewDisLockRedis(key string, redis *redis.Pool) *DisLockRedis {
this := &DisLockRedis{
key: key,
ttl: 30,
redis: redis,
}
return this
}
//上锁
func (this *DisLockRedis) TryLock() (err error) {
if err = this.grant(); err != nil {
return
}
ctx, cancelFun := context.WithCancel(context.TODO())
this.cancelFun = cancelFun
//自动续期
this.renew(ctx)
this.isLocked = true
return nil
}
//释放锁
func (this *DisLockRedis) Unlock() (err error) {
var res int
if this.isLocked {
if res, err = redis.Int(this.redisConn().Do("DEL", this.key)); err != nil {
if this.debug {
log.Println(err.Error())
}
return errors.New("释放锁失败")
}
if res == 1 {
//释放成功,取消自动续租
this.cancelFun()
return
}
}
return errors.New("释放锁失败")
}
//自动续期
func (this *DisLockRedis) renew(ctx context.Context) {
go func() {
for {
select {
case <-ctx.Done():
return
default:
res, err := redis.Int(this.redisConn().Do("EXPIRE", this.key, this.ttl))
if this.debug {
if err != nil {
log.Println("锁自动续期失败:", err)
}
if res != 1 {
log.Println("锁自动续期失败")
}
}
}
time.Sleep(time.Duration(this.ttl/3) * time.Second)
}
}()
}
//创建租约
func (this *DisLockRedis) grant() (err error) {
if res, err := redis.String(this.redisConn().Do("SET", this.key, "xxx", "NX", "EX", this.ttl)); err != nil {
if this.debug {
log.Println(err)
}
} else {
if res == "OK" {
return nil
}
}
return errors.New("上锁失败")
}
func (this *DisLockRedis) redisConn() redis.Conn {
return this.redis.Get()
}
func (this *DisLockRedis) Debug() *DisLockRedis {
this.debug = true
return this
}
注意
每次使用锁,都必须调用NewDisLockRedis新建对象
如有问题欢迎指正
本作品采用《CC 协议》,转载必须注明作者和本文链接
推荐文章: