Redis分布式锁 注解和代码形式

Redis分布式锁 注解和代码形式


spring-boot-distributed-redisson项目进行讲解
关于分布式锁的介绍大家肯定可以说上很多,这里仅作一些补充:

针对分布式服务想要去对共享资源进行上锁,之前使用的线程级别的锁只能作用在当前服务下,通俗的讲就是一个jvm中,分布式架构下肯定是不行的,其实本质来讲两者区别不大

redisson


        RLock rLock = redisson.getLock(lockName);
        boolean getLock = false;
        try {
        	//param1:等待时间
        	//param2:锁的有效时间
        	//param3:时间单位
            getLock = rLock.tryLock( waitTime,leaseTime, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            log.error("获取Redisson分布式锁[异常],lockName=" + lockName, e);
            e.printStackTrace();
            return false;
        }
        return getLock;
        RLock rLock = redisson.getLock(lockName); //锁有效时间采用默认时间30秒
        rLock.lock();

如果您系统中锁的使用比较频繁 可以通过自定义注解的形式结合AOP进行使用

@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DistributedLock {

    /**
     * 锁的名称
     */
    String value() default "redisson_lock";

    /**
     * 锁的有效时间
     */
    int leaseTime() default 10;
}
@Aspect
@Component
@Slf4j
public class DistributedLockHandler {

    @Autowired
    RedissonLock redissonLock;


    @Around("@annotation(distributedLock)")
    public void around(ProceedingJoinPoint joinPoint, DistributedLock distributedLock) {
        log.info("[开始]执行RedisLock环绕通知,获取Redis分布式锁开始");
        //获取锁名称
        String lockName = distributedLock.value();
        //获取超时时间,默认10秒
        int leaseTime = distributedLock.leaseTime();
        redissonLock.lock(lockName, leaseTime);
        try {
            log.info("获取Redis分布式锁[成功],加锁完成,开始执行业务逻辑...");
            //执行方法
            joinPoint.proceed();
        } catch (Throwable throwable) {
            log.error("获取Redis分布式锁[异常],加锁失败", throwable);
            throwable.printStackTrace();
        } finally {
            //如果该线程还持有该锁,那么释放该锁。如果该线程不持有该锁,说明该线程的锁已到过期时间,自动释放锁
            if (redissonLock.isHeldByCurrentThread(lockName)) {
                redissonLock.unlock(lockName);
            }
        }
        log.info("释放Redis分布式锁[成功],解锁完成,结束业务逻辑...");
    }
}

这样的情况下存在我们设置一个lock的有效时间为10秒,但是业务代码执行了20秒,导致其他线程获取到了锁,同样也是存在问题的,那么需要我们对key进行续期,Redisson有一个看门狗机制,自动进行key的续期,原理是:redisson在获取锁之后,会维护一个看门狗线程,当锁即将过期还没有释放时,不断的延长锁key的生存时间 需要主要以下几点:

  • 看门狗启动后,对整体性能也会有一定影响,默认情况下看门狗线程是不启动的。如果使用redisson进行加锁的同时设置了锁的过期时间,也会导致看门狗机制失效。

  • redisson在获取锁之后,会维护一个看门狗线程,在每一个锁设置的过期时间的1/3处,如果线程还没执行完任务,则不断延长锁的有效期。看门狗的检查锁超时时间默认是30秒,可以通过 Config.lockWatchdogTimeout 参数来改变。

  • 加锁的时间默认是30秒,如果加锁的业务没有执行完,那么每隔 30 ÷ 3 = 10秒,就会进行一次续期,把锁重置成30秒,保证解锁前锁不会自动失效。

  • 那万一业务的机器宕机了呢?如果宕机了,那看门狗线程就执行不了了,就续不了期,那自然30秒之后锁就解开了呗。

public String redissonLockWatchDog(String key){
        //获取一把锁,只要锁的名字一样就是同一把锁
        RLock lock = singleRedissonClient.getLock(key);
        //1).redisson的自动续期,如果业务超长,运行期间自动续上30s,不用担心业务时间长,锁自动过期被删掉
        //2).加锁得业务只要运行完成,就不会给当前锁续期,即使不手动解锁,锁默认在30s后自动删除
        lock.lock();
        try {
            System.out.println("加锁成功,开始执行业务: " + lock.getName() + " 时间: " + getDateStr(new Date()));
            Thread.sleep(65000);
            System.out.println("加锁成功,结束执行业务: " + lock.getName() + " 时间: " + getDateStr(new Date()));
        } catch (Exception e){
            System.out.println("报错了: " + e);
        } finally{
            //解锁
            System.out.println("释放锁"+lock.getName());
            lock.unlock();
        }
        return "hello";
    }
  • watch dog 机制启动,且代码中没有释放锁操作时,watch dog 会不断的给锁续期;
  • 如果程序释放锁操作时因为异常没有被执行,那么锁无法被释放,所以释放锁操作一定要放到 finally {} 中;
  • 要使 watchLog机制生效 。只要不穿leaseTime即可
  • watchlog的延时时间 可以由 lockWatchdogTimeout指定默认延时时间,但是不要设置太小。如100
  • watchdog 会每 lockWatchdogTimeout/3时间,去延时。
  • watchdog 通过 类似netty的 Future功能来实现异步延时
  • watchdog 最终还是通过 lua脚本来进行延时

参考链接:
Redis分布式锁
Redisson的看门狗机制

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Redis分布式锁是一种基于Redis分布式锁实现方式,通过Redis实现对分布式系统中共享资源的互斥访问。具体注解如下: 1. 分布式锁的实现方式:使用Redis的setnx命令实现分布式锁,即在Redis中创建一个键值对,当键不存在时才能创建成功,表示获取锁成功,否则获取锁失败。在释放锁时,删除对应的键值对即可。 2. 锁的唯一性:分布式锁需要保证锁的唯一性,可以通过在锁的键名中添加全局唯一的标识符,如应用程序的名称、服务器的IP地址等。 3. 锁的过期时间:为了防止死锁,需要给锁设置过期时间,可通过Redis的expire命令设置键的过期时间。 4. 锁的重入性:分布式锁需要支持重入,即同一个线程可以多次获取同一个锁,需要在锁的值中添加线程ID等标识符,以便在释放锁时判断是否是同一个线程。 5. 锁的释放方式:分布式锁需要支持手动和自动释放,手动释放即调用del命令删除锁的键值对,自动释放即设置锁的过期时间,在过期后Redis会自动删除键值对。 6. 锁的容错性:分布式锁需要保证容错性,即在获取锁失败时不会导致系统异常或数据丢失,可以通过重试机制或设置超时时间来实现。 7. 锁的并发性:分布式锁需要保证并发性,即在高并发情况下多个线程可以同时获取锁,需要使用Redis的lua脚本实现原子性操作,避免并发问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值