欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

基于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
}