分布式锁-注解式redisson分布式锁

本文详细介绍了如何使用Redisson实现注解式的分布式锁,通过AOP切面在方法执行前后进行加锁和解锁操作,确保在分布式环境中对数据的互斥访问。同时,文章讨论了Redisson在主从复制场景下可能出现的问题,并给出了适用的场景,如多实例服务间的同步操作和分布式定时任务的独占执行。此外,提供了引入依赖、编写注解、切面逻辑及示例代码,帮助读者理解和实践。
摘要由CSDN通过智能技术生成

原理概述

利用aop特性,编制一个环绕切面给加了注解的方法体上,每次执行该方法时,首先进入切面进行加锁,执行完毕后回到切面,进行解锁。
redisson本身支持redis的集群部署,而redisson的缺陷就是,当线程A对master的节点加锁成功,二master节点会异步复制到slave节点,而恰在此时,还没有复制到slave节点时,master节点发生奔溃宕机,slave节点切换为主节点的时候,线程B也加锁成功,此时,两个线程都获得了锁,都去执行一段代码,会产生一些问题。

适用场景

分布式部署的情况下,单个服务启动多实例的情况下,需要对相同数据进行操作。
在定时任务没有单独分离出一个服务,而定时任务所在的服务启动了多实例,要保证定时任务只会被一个示例执行。
。。。。

引入依赖

<!--	引入redisson的springboot启动器	-->
<!--	版本需要格外注意,需要去mvn仓库里查询redisson支持的starter-data-redis版本
			本次使用的starter-parent是2.1.3.RELEASE,最高到3.9.1支持才支持,主要是适配redis的版本-->
		<dependency>
			<groupId>org.redisson</groupId>
			<artifactId>redisson-spring-boot-starter</artifactId>
			<version>3.9.1</version>      
		</dependency>
		<!--	aop切面依赖	-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-aop</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-redis</artifactId>
		</dependency>

编写注解

加入enable是为了控制定时任务是否执行,而不是控制是否要加锁处理,具体可以根据业务需求,修改切面类的逻辑。

package com.ai.sx.modules.yitiji.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RedisLock {

    //锁的名称
    String lockName();

    //是否生效,1生效,0不生效-即使加锁成功,扔不执行代码
    String enable();
    //锁的失效时间,默认3秒,单位秒
    long leaseTime() default 3;

}

编写切面

进入切面后,加锁成功,则执行任务,加锁失败,则什么也不干。
具体加锁失败是需要循环尝试加锁,还是中断,需要修改切面的逻辑;
需要循环尝试加锁,则使用do…while循环,进行循环尝试,为了缓解redis压力,降低多个线程抢锁的可能,可以在每次尝试加锁失败后,让线程睡眠一定的时间。

package com.ai.sx.modules.yitiji.aspect;

import com.ai.sx.modules.yitiji.annotation.RedisLock;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

@Aspect
@Component
public class RedissonLockAspect {

    @Autowired
    private RedissonClient redissonClient;

    @Around("@annotation(redisLock)")
    public Object around(ProceedingJoinPoint joinPoint, RedisLock redisLock) throws Throwable {
        if (!"1".equals(redisLock.enable())){
            return null;
        }
        String lockName = redisLock.lockName();
        long leaseTime = redisLock.leaseTime();
        RLock lock = redissonClient.getLock(lockName);
        Object result = null;
        if (lock.tryLock(0,leaseTime, TimeUnit.SECONDS)){
            try {
                //执行方法
                result = joinPoint.proceed();
            } finally {
                if (lock != null && lock.isHeldByCurrentThread()){
                    lock.unlock();
                }
            }
        }
        return result;
    }

}

使用

模拟了定时删除临时目录的定时任务,enable控制定时任务是否执行。

package com.ai.sx.modules.yitiji.task;

import com.ai.sx.modules.yitiji.annotation.RedisLock;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.io.File;

@Component
@Slf4j
public class ImageTask {

    @Value("${yitiji.img.base.path}")
    private String IMG_BASE_PATH;

    @RedisLock(lockName = "deletedImagesTask",enable = "0")
    @Scheduled(cron = "0/5 * * * * ?")
    public void deletedImagesTask(){
        if(!StringUtils.isBlank(IMG_BASE_PATH)){
            File file = new File(IMG_BASE_PATH);
            if(file.exists()){
                for (File f : file.listFiles()){
                    deleteFiles(f);
                }
            }
        }
    }

    private void deleteFiles(File file){
        if (file.isDirectory()){
            File[] files = file.listFiles();
            for (File f : files){
                deleteFiles(f);
            }
        }
        if (file.delete()){
            log.info(file.getAbsolutePath()+"已被删除");
        }else {
            log.info(file.getAbsolutePath()+"删除失败");
        }
    }
}

参考阅读

注解式 redisson分布式锁
Redis锁机制的几种实现方式
Redis 分布式锁的正确实现方式讨论
Redission分布式锁原理

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值