redis实现分布式锁

6 篇文章 0 订阅
6 篇文章 0 订阅
// 分布式锁实现使用的版本
// springBoot的版本:org.springframework.boot:spring-boot:1.5.8.RELEASE
// redis的版本:org.springframework.data:spring-data-redis:1.8.8.RELEASE
// lombok的版本:org.projectlombok:lombok:1.18.6 

// 标识springBoot启动时会扫描该类并创建实例放入spring容器
@Component
// 引入lombokjar包,才能使用的注解
@Slf4j
public class DistributedLock {
    private static final String LOCK_PREFIX = "REDIS_LOCK_";
    //加锁失效时间,单位:秒
    public static final int LOCK_EXPIRE = 3;

    // 此处注入的RedisTemplate实例信息,可以查看Redis与SpringBoot融合这篇工具类博客
    // 文章地址:https://carefulhuo.github.io/posts/c51e64d0/
    @Autowired
    RedisTemplate redisTemplate;

    /**
     * 尝试获取分布式锁
     *
     * @param lockKey    锁
     * @param requestId  请求标识
     * @return 是否获取成功
     */
    public boolean tryLock(String lockKey, String requestId) {
        log.info("{}开始加锁,requestId:{}", lockKey, requestId);
        String lock = LOCK_PREFIX + lockKey;
        // 利用lambda表达式
        // setNX英文全称:SET if Not Exists
        // setNX含义:如果key存在,设置失败返回0;如果key不存在,设置成功返回1
        // setNX方法:原子性的,但是该命令不能设置超时时间
        // expire方法:设置超时时间,防止死锁
        // 注意其中的RedisCallback,当redis有多个实例时,数据进行主从同步时,但主redis挂掉,随机选择一个从redis作为主redis时,不会发生多个请求获取到锁。
        // 原理如下:加锁时,会向多半的节点发送setNX命令,如果多半节点成功,则算加锁成功,那么释放锁的时候,需要想所有节点发送del命令
        return (Boolean) redisTemplate.execute((RedisCallback) connection -> {
            if (connection.setNX(lock.getBytes(), requestId.getBytes())) {
                connection.expire(lock.getBytes(), LOCK_EXPIRE);
                return true;
            }
            return false;
        });
    }

    /**
     * 释放分布式锁
     *
     * @param lockKey   锁
     * @param requestId 请求标识
     * @return 是否释放成功
     */
    public boolean releaseLock(String lockKey, String requestId) {
        log.info("{}开始释放锁,requestId:{}", lockKey, requestId);
        String lock = LOCK_PREFIX + lockKey;
        final String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end ";

        // eval命令的参数(lua脚本,返回类型,脚本的参数个数,redis的key,redis的value)
        // redis执行lua脚本是原子性的
        return (Boolean)redisTemplate.execute((RedisCallback) connection -> {
            return connection.eval(script.getBytes(), ReturnType.BOOLEAN ,1, lock.getBytes(Charset.forName("UTF-8")), requestId.getBytes(Charset.forName("UTF-8")));
        });
    }
}

上述释放redis锁需要redis执行的lua脚本,表述的代码含义:

-- if 中的比较如果是true , 那么 执行 del 并返回del结果;如果 if 结果为false 直接返回 0 。
if redis.call('get', KEYS[1]) == ARGV[1] 
    then 
        return redis.call('del', KEYS[1]) 
    else 
        return 0 
end
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值