reids实现分布式锁,上锁解锁异步业务代码实现。

reids实现分布式锁

由于(业务上锁和解锁异步),导致我需要使用redis 自定义锁.

如果上锁和解锁是同步,推荐使用Redisson组件,解决了锁的续时,服务宕机后的死锁问题,锁的可冲入,

这里有两个问题:

  • 为什么要设置锁的过期时间?
  • 回答:因为防止出现死锁。
  • 锁的过期时间设置多久合适?
  • 回答: 由于自己实现锁续期很麻烦,且场景bug很多,所以redisson有一个看门狗watchDog机制,源码默认是上锁是30s,当每次执行到 过期时间三分之一 (10s)则给锁进行续命。
  • 解锁过程中服务器宕机导致没有解锁成功?
  • 回答:正常异常情况finally可以保证解锁,但是服务器宕机造成死锁,redisson有一个watchDog机制当服务器宕机后watchDog则也随之死亡。
    这不是重点,科普一下,只是想告诉大家,redisson确实很强大,能用则用,不要自己搞。
    在这里插入图片描述
    过程:
    就是这么简单,我们只需要通过它的api中的lock和unlock即可完成分布式锁,他帮我们考虑了很多细节:

答案:核心内容

  • 1:redisson所有指令都通过lua脚本执行,redis支持lua脚本原子性执行

  • 2:redisson设置一个key的默认过期时间为30s,如果某个客户端持有一个锁超过了30s怎么办? redisson中有一个watchdog的概念,翻译过来就是看门狗,它会在你获取锁之后,每隔10秒帮你把key的超时时间设为30s 这样的话,就算一直持有锁也不会出现key过期了,其他线程获取到锁的问题了。

  • 3:redisson的“看门狗”逻辑保证了没有死锁发生。 (如果机器宕机了,看门狗也就没了。此时就不会延长key的过期时间,到了30s之后就会自动过期了,其他线程可以获取到锁)

ap方法如下
在这里插入图片描述

开始正题 为什么要自定义锁,因为业务上异步解锁,要考虑地方是异常解锁情况

自定义Redis 实现分布式锁 需求实现 同一时间一条词条只允许一个管理编辑,其他管理员访问改词条则访问不成功

1.上锁 : 设置过期时间为了保证解决死锁问题,保证上锁和过期时间的原子性,此外还要考虑浏览器突然关闭没有正常执行解锁操作。由于设置了过期时间,但是用户没有在指定时间内提交业务,所以锁的续期问题需要解决。

2.锁续期:锁的续期 ,当停留在加锁页面,前端定时轮询调用锁的续期接口,保证用户提交正常。设置设置最长停留时间,超过则不调用续时解决锁无限制一直续期问题。

3.解锁:保证上锁的用户和解锁的用户是一个人同一次操作,如何保证,当上锁时候会自定义一个会话id(uuid),上锁时将会话id存放在value,解锁的时候需要带着会话id过来,判断会话id是否是一直一致,解决了多用户误解锁问题。

补充:有一些场景 解锁为什么要给先给锁续期 ,保证方法执行完时间够用,为了解决查到锁的一瞬锁失效了,B线程抢占到锁了,查到锁之后会执行删除锁。此时删除的锁是B的,做好解决办法就是在解锁 和提交更新sql 之前锁是百分之百存在的不会过期 就解决了。

maven

<!-- redis-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
package com.excel.batch.redislock.controller;

import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.ObjectUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.TimeUnit;

/**
 * @Author: 宋忠瑾
 * @Description: redis实现分布式锁,因为业务拆分的
 * @Date: Create in 17:33 2021/9/7
 */
@RestController
@Slf4j
public class RedisDistributedLock {
    /**
     * redis上锁到期时间
     */
    public static final int EXPIRATION_TIME = 10;
    /**
     * redis上锁续期时间
     */
    public static final int RENEWAL_TIME = 30;
    /**
     * redis操作模板
     */
    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * redis上锁判断 是否存在
     *
     * @return 成功但会uuid 失败返回null
     * @Description 上锁
     * @Method 上锁判断 是否存在
     * @Author 宋忠瑾
     * @Date 2021/9/7 17:35
     */
    @GetMapping("tryLock")
    public String testLocktime() {
        //每一次访问生成一个会话id
        String uuid = UUID.fastUUID().toString();
        //上锁
        Boolean lock = redisTemplate.opsForValue()
                .setIfAbsent("bravo1988_distributed_lock", uuid, EXPIRATION_TIME, TimeUnit.SECONDS);
        if (lock) {
            log.info("上锁");
            return uuid;
        } else {
            log.error("上锁失败");
            return null;
        }
    }


    /**
     * redis锁续期
     *
     * @param key 键值
     * @return Boolean
     * @Description 锁续期
     * @Method 锁续期
     * @Author 宋忠瑾
     * @Date 2021/9/7 17:36
     */
    @GetMapping("tryLockRenew")
    public Boolean testLockSuccess(@RequestParam String key) {
        if (redisTemplate.hasKey(key)) {
            redisTemplate.expire(key, RENEWAL_TIME, TimeUnit.SECONDS);
            //上锁
            log.info("续期成功:{}", true);
            return true;
        } else {
            //上锁失败
            log.info("续期失败: 当前锁不存在");
            return false;
        }
    }

    /**
     * 解锁
     *
     * @param key   键值
     * @param value 存放的uuid
     * @return Boolean
     * @Description 判断用户生成的uuid
     * @Method
     * @Author 宋忠瑾
     * @Date 2021/9/7 17:37
     */
    @GetMapping("unLock")
    public Boolean testUnLock(@RequestParam String key, @RequestParam String value) {
    //先给锁续期 保证方法执行完时间够用,为了解决查到锁的一瞬锁失效了,B线程抢占到锁了,查到锁之后会执行删除锁
     redisTemplate.expire(key, RENEWAL_TIME, TimeUnit.SECONDS);
        //获取当前redis key的value
        Object redisValue = redisTemplate.opsForValue().get(key);
        if (ObjectUtil.isNotNull(redisValue) && redisValue.toString().equals(value)) {
            //解锁 判断是否是同一个人
            redisTemplate.delete(key);
            log.info("解锁成功");
            return true;
        } else {
            log.info("当前用户uuid不一致或当前不存在锁,解锁失败");
            return false;
        }
    }
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值