SpringBoot+Redisson+AOP实现分布式锁,防重复提交

一 、引入jar包

<!-- springboot 对redis的支持-->
<dependency>
	<groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- springboot 对aop的支持 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- redisson -->
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.9.1</version>
</dependency>

二、配置文件

#redis配置
spring.redis.host=39.100.91.122
spring.redis.port=6379
spring.redis.database=0
spring.redis.timeout=5000ms
#redisson客户端连接超时时间(ms)
redisson.timeout=10000

三、Redisson代码

3.1 分布式锁接口
/**
 * 分布式锁接口
 */
public interface DistributeLocker {

    /**
     * 加锁
     * @param lockKey key
     */
    void lock(String lockKey);

    /**
     * 释放锁
     *
     * @param lockKey key
     */
    void unlock(String lockKey);

    /**
     * 加锁锁,设置有效期
     *
     * @param lockKey key
     * @param timeout 有效时间,默认时间单位在实现类传入
     */
    void lock(String lockKey, int timeout);

    /**
     * 加锁,设置有效期并指定时间单位
     * @param lockKey key
     * @param timeout 有效时间
     * @param unit    时间单位
     */
    void lock(String lockKey, int timeout, TimeUnit unit);

    /**
     * 尝试获取锁,获取到则持有该锁返回true,未获取到立即返回false
     * @param lockKey
     * @return true-获取锁成功 false-获取锁失败
     */
    boolean tryLock(String lockKey);

    /**
     * 尝试获取锁,获取到则持有该锁leaseTime时间.
     * 若未获取到,在waitTime时间内一直尝试获取,超过watiTime还未获取到则返回false
     * @param lockKey   key
     * @param waitTime  尝试获取时间
     * @param leaseTime 锁持有时间
     * @param unit      时间单位
     * @return true-获取锁成功 false-获取锁失败
     */
    boolean tryLock(String lockKey, long waitTime, long leaseTime, TimeUnit unit)
            throws InterruptedException;

    /**
     * 锁是否被任意一个线程锁持有
     * @param lockKey
     * @return true-被锁 false-未被锁
     */
    boolean isLocked(String lockKey);


}
3.2 redisson实现分布式锁
/**
 * redisson实现分布式锁接口
 */
public class RedissonDistributeLocker implements DistributeLocker {

    private RedissonClient redissonClient;

    public RedissonDistributeLocker(RedissonClient redissonClient) {
        this.redissonClient = redissonClient;
    }

    @Override
    public void lock(String lockKey) {
        RLock lock = redissonClient.getLock(lockKey);
        lock.lock();
    }

    @Override
    public void unlock(String lockKey) {
        RLock lock = redissonClient.getLock(lockKey);
        lock.unlock();
    }

    @Override
    public void lock(String lockKey, int leaseTime) {
        RLock lock = redissonClient.getLock(lockKey);
        lock.lock(leaseTime, TimeUnit.MILLISECONDS);
    }

    @Override
    public void lock(String lockKey, int timeout, TimeUnit unit) {
        RLock lock = redissonClient.getLock(lockKey);
        lock.lock(timeout, unit);
    }

    @Override
    public boolean tryLock(String lockKey) {
        RLock lock = redissonClient.getLock(lockKey);
        return lock.tryLock();
    }

    @Override
    public boolean tryLock(String lockKey, long waitTime, long leaseTime,
                           TimeUnit unit) throws InterruptedException {
        RLock lock = redissonClient.getLock(lockKey);
        return lock.tryLock(waitTime, leaseTime, unit);
    }

    @Override
    public boolean isLocked(String lockKey) {
        RLock lock = redissonClient.getLock(lockKey);
        return lock.isLocked();
    }


}
3.3 redisson锁的工具类
/**
 * redisson锁工具类
 */
public class RedissonLockUtils {

    private static DistributeLocker locker;

    public static void setLocker(DistributeLocker locker) {
        RedissonLockUtils.locker = locker;
    }

    public static void lock(String lockKey) {
        locker.lock(lockKey);
    }

    public static void unlock(String lockKey) {
        locker.unlock(lockKey);
    }

    public static void lock(String lockKey, int timeout) {
        locker.lock(lockKey, timeout);
    }

    public static void lock(String lockKey, int timeout, TimeUnit unit) {
        locker.lock(lockKey, timeout, unit);
    }

    public static boolean tryLock(String lockKey) {
        return locker.tryLock(lockKey);
    }

    public static boolean tryLock(String lockKey, long waitTime, long leaseTime,
                                  TimeUnit unit) throws InterruptedException {
        return locker.tryLock(lockKey, waitTime, leaseTime, unit);
    }

    public static boolean isLocked(String lockKey) {
        return locker.isLocked(lockKey);
    }

}
3.4 初始化Redisson
/**
 * redisson bean管理
 */
@Configuration
public class RedissonConfig {

    @Autowired
    private Environment env;

    /**
     * Redisson客户端注册
     * 单机模式
     */
    @Bean(destroyMethod = "shutdown")
    public RedissonClient createRedissonClient() {
        Config config = new Config();
        SingleServerConfig singleServerConfig = config.useSingleServer();
        singleServerConfig.setAddress("redis://" + env.getProperty("spring.redis.host") + ":" + env.getProperty("spring.redis.port"));
        singleServerConfig.setTimeout(Integer.valueOf(env.getProperty("redisson.timeout")));
        return Redisson.create(config);
    }

    /**
     * 分布式锁实例化并交给工具类
     * @param redissonClient
     */
    @Bean
    public RedissonDistributeLocker redissonLocker(RedissonClient redissonClient) {
        RedissonDistributeLocker locker = new RedissonDistributeLocker(redissonClient);
        RedissonLockUtils.setLocker(locker);
        return locker;
    }

}

至此,便可以通过RedissonLockUtils来实现分布式锁。

四、AOP代码

4.1 分布式锁注解
/**
 * 分布式锁注解
 * 添加到方法 ,修饰以@RequestBody JSON对象接收参数的方法
 */
@Target(ElementType.METHOD) //注解在方法
@Retention(RetentionPolicy.RUNTIME)
public @interface RedissonLockAnnotation {

    /**
     * 指定组成分布式锁的key,以逗号分隔。
     * 如:keyParts="name,age",则分布式锁的key为这两个字段value的拼接
     * key=params.getString("name")+params.getString("age")
     */
    String keyParts();
}
4.2 aop实现
/**
 * 分布式锁/防重复提交 aop
 */
@Aspect
@Component
@Slf4j
public class RedissonLockAop {

    /**
     * 切点,拦截被 @RedissonLockAnnotation 修饰的方法
     */
    @Pointcut("@annotation(com.icbc.cxlab.rtc.annotation.RedissonLockAnnotation)")
    public void redissonLockPoint() {
    }

    @Around("redissonLockPoint()")
    @ResponseBody
    public APIResult checkLock(ProceedingJoinPoint pjp) throws Throwable {
        //当前线程名
        String threadName = Thread.currentThread().getName();
        log.info("线程{}------进入分布式锁aop------", threadName);
        //获取参数列表
        Object[] objs = pjp.getArgs();
        //因为只有一个JSON参数,直接取第一个
        JSONObject param = (JSONObject) objs[0];
        //获取该注解的实例对象
        RedissonLockAnnotation annotation = ((MethodSignature) pjp.getSignature()).
                getMethod().getAnnotation(RedissonLockAnnotation.class);
        //生成分布式锁key的键名,以逗号分隔
        String keyParts = annotation.keyParts();
        StringBuffer keyBuffer = new StringBuffer();
        if (StringUtils.isEmpty(keyParts)) {
            log.info("线程{} keyParts设置为空,不加锁", threadName);
            return (APIResult) pjp.proceed();
        } else {
            //生成分布式锁key
            String[] keyPartArray = keyParts.split(",");
            for (String keyPart : keyPartArray) {
                keyBuffer.append(param.getString(keyPart));
            }
            String key = keyBuffer.toString();
            log.info("线程{} 要加锁的key={}", threadName, key);
            //获取锁
            if (RedissonLockUtils.tryLock(key, 3000, 5000, TimeUnit.MILLISECONDS)) {
                try {
                    log.info("线程{} 获取锁成功", threadName);
                    return (APIResult) pjp.proceed();
                } finally {
                    RedissonLockUtils.unlock(key);
                    log.info("线程{} 释放锁", threadName);
                }
            } else {
                log.info("线程{} 获取锁失败", threadName);
                return new APIResult(-9, "请求超时");
            }
        }

    }
}

五、分布式锁应用

	@PostMapping(value = "testLock", consumes = "application/json")
    @RedissonLockAnnotation(keyParts = "name,age")
    public APIResult testLock(@RequestBody JSONObject params) {
        /**
         * 分布式锁key=params.getString("name")+params.getString("age");
         * 此时name和age均相同的请求不会出现并发问题
         */
        //TODO 业务处理
        return new APIResult(0, "SUCCESS");
    }

记录成长,热爱生活!

  • 1
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值