基于redis实现分布式锁
程序员文章站
2022-07-05 13:22:19
...
package redislock
import (
"errors"
"time"
"github.com/go-redis/redis"
)
var (
REDIS_LOCK_EXIST error = errors.New("锁已经存在")
DEL_REDIS_LOCK_ERROR error = errors.New("解锁失败")
REDIS_LOCK_WAIT_TIME_OUT_EXIT error = errors.New("尝试获取锁等待超时并退出")
)
type RedisConfig struct {
RedisAddr string
Password string
Db int
RedisLockTimeOut time.Duration //锁的有效时间,超时释放
RedisLockWaitTimeOut time.Duration //获取锁阻塞时间,超时放弃
}
type RedisLock struct {
redisClient *redis.Client
redisLockTimeOut time.Duration
redisLockWaitTimeOut time.Duration
}
func newRedisLock(redisConfig *RedisConfig) *RedisLock {
redisClient := redis.NewClient(&redis.Options{
Addr: redisConfig.RedisAddr,
Password: redisConfig.Password,
DB: redisConfig.Db,
PoolSize: 30,
DialTimeout: 2 * time.Second,
ReadTimeout: 2 * time.Second,
WriteTimeout: 2 * time.Second,
PoolTimeout: 10 * time.Second,
IdleTimeout: 3600 * time.Second,
IdleCheckFrequency: 300 * time.Second,
})
return &RedisLock{
redisClient: redisClient,
redisLockTimeOut: redisConfig.RedisLockTimeOut,
redisLockWaitTimeOut: redisConfig.RedisLockWaitTimeOut,
}
}
func (this *RedisLock) Lock(key string) error {
val := this.redisClient.SetNX(key+".lock", time.Now().Unix(), this.redisLockTimeOut)
if val.Val() == true { //成功获取到锁
return nil
} else {
return REDIS_LOCK_EXIST //锁已经存在
}
}
func (this *RedisLock) Unlock(key string) error {
val := this.redisClient.Del(key + ".lock")
if val.Val() == 0 {
log.Errorf("key = %s err = %s", key, DEL_REDIS_LOCK_ERROR)
return DEL_REDIS_LOCK_ERROR //解锁失败
}
return nil
}
func (this *RedisLock) TryGetLock(key string) error {
nowTime := time.Now().Unix()
for {
err := this.Lock(key)
if err == nil { //成功获取到锁就直接返回
return nil
} else {// 否则阻塞一段时间,期间每隔0.2s尝试获取锁,超时退出
if err = this.IsWaitTimeOut(nowTime); err == nil {
time.Sleep(200 * time.Millisecond)
} else {
return err
}
}
}
}
func (this *RedisLock) IsWaitTimeOut(nowTime int64) error {
//判断是否等待超时
if time.Now().Unix() >= nowTime+int64(this.redisLockWaitTimeOut/time.Second) {
return REDIS_LOCK_WAIT_TIME_OUT_EXIT //尝试获取锁等待超时并退出
}
return nil
}