我理解的分布式锁:
一、为了不单持有锁的进程奔溃而没法释放锁,因此必须可以为锁设定过时时间自动释放锁资源 二、使用TTL检查锁是否成功的被设置过时时间,若是返回-1(未被设置)的话,使用Expire为其设定过时时间 三、在释放锁的时候须要使用Watch命令,确保监测的值在事务执行时时未被改变,若是其余进程修改了锁,会触发事务异常,而后从新执行Watch。git
分享一个我写的Redis封装类,仅实现了链接和锁github
package dao
import (
"time"
"github.com/go-redis/redis/v7"
uuid "github.com/satori/go.uuid"
)
type Rds struct {
Client *redis.Client
AcquireTimeout int32
LockTimeout int32
}
func NewRds(host, passwd string, db int) (rds *Rds, err error) {
rdsOpt := &redis.Options{
Addr: host,
Password: passwd,
DB: db,
}
client := redis.NewClient(rdsOpt)
_, err = client.Ping().Result()
if err != nil {
return
}
rds = &Rds{Client: client, AcquireTimeout: 10, LockTimeout: 10}
return
}
func (r *Rds) AcquireLockWithTimeout(key string) (identifier string, b bool) {
identifier = uuid.NewV4().String()
lockname := "lock:" + key
end := time.Now().Add(time.Second * time.Duration(r.AcquireTimeout))
for time.Now().Before(end) {
if r.Client.SetNX(lockname, identifier, time.Second*time.Duration(r.LockTimeout)).Val() {
// 若是key不存在,并成功设置了key
b = true
return
} else if r.Client.TTL(lockname).Val() == -1 {
// 若是key存在,可是没有剩余时间
r.Client.Expire(lockname, time.Second*time.Duration(r.LockTimeout))
}
time.Sleep(time.Microsecond)
}
return
}
func (r *Rds) ReleaseLock(key, identifier string) (b bool) {
lockname := "lock:" + key
txf := func(tx *redis.Tx) error {
v, err := tx.Get(lockname).Result()
if err != nil {
return err
}
_, err = tx.Pipelined(func(pipe redis.Pipeliner) error {
if v == identifier {
pipe.Del(lockname)
b = true
}
return nil
})
return err
}
for {
err := r.Client.Watch(txf, lockname)
if err == nil {
break
} else if err == redis.TxFailedErr {
glog.Error(err)
}
}
return
}