項目最近涉及到自動彈縮,因此涉及到分佈式鎖相關的處理;
項目redis採用go-redis
庫(點擊跳轉)進行操作;因此基於此實現了一個分佈式鎖,做一個記錄;
如有缺陷,歡迎指正
- redis分佈式鎖的實現是基於
SETNX
命令的特性來完成的, 即:僅首次設置值時才能夠設置成功
同時做了一定的保護,防止惡意不設置過期時間導致永久佔用鎖的情況。 - 釋放鎖時,使用了
MULTI/EXEC
,同時WATCH
鎖保證不會錯誤的釋放鎖;
package fwRedis
import (
"errors"
"fmt"
"github.com/go-redis/redis/v7"
"github.com/satori/go.uuid"
"leiax00.com/fxWeb/util"
"time"
)
func GetLock(lockName string, acquireTimeout, lockTimeOut time.Duration) (string, error) {
code := uuid.NewV4().String()
endTime := util.FwTimer.CalcMillis(time.Now().Add(acquireTimeout))
for util.FwTimer.CalcMillis(time.Now()) <= endTime {
if success, err := fwRedisClient.SetNX(lockName, code, lockTimeOut).Result(); err != nil && err != redis.Nil {
return "", err
} else if success {
return code, nil
} else if fwRedisClient.TTL(lockName).Val() == -1 { //-2:失效;-1:無過期;
fwRedisClient.Expire(lockName, lockTimeOut)
}
time.Sleep(time.Millisecond)
}
return "", errors.New("timeout")
}
//var count = 0 // test assist
func ReleaseLock(lockName, code string) bool {
txf := func(tx *redis.Tx) error {
if v, err := tx.Get(lockName).Result(); err != nil && err != redis.Nil {
return err
} else if v == code {
_, err := tx.Pipelined(func(pipe redis.Pipeliner) error {
//count++
//fmt.Println(count)
pipe.Del(lockName)
return nil
})
return err
}
return nil
}
for {
if err := fwRedisClient.Watch(txf, lockName); err == nil {
return true
} else if err == redis.TxFailedErr {
fmt.Println("watch key is modified, retry to release lock. err:", err.Error())
} else {
fmt.Println("err:", err.Error())
return false
}
}
}
完整代碼實例 ==> please click me