原子性加鎖
加鎖問題,為避免死鎖,需要加鎖的時候為鎖設定一個存活時間,過了這個時間,鎖自動被釋放掉
安全釋放鎖
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 協議》,轉載必須註明作者和本文連結