[Java]Redis分布式锁

2021 年 11 月 18 日 星期四(已编辑)
/ ,
9
这篇文章上次修改于 2024 年 4 月 11 日 星期四,可能部分内容已经不适用,如有疑问可询问作者。

[Java]Redis分布式锁

  1. RedisLock.java ``` java package com.bf.form.common.aspect;

import org.springframework.core.Ordered; import org.springframework.core.annotation.Order;

import java.lang.annotation.*;

/

  • @Author: hyakutake
  • @Date: 2022/05/23 10:00
  • @Description: / @Documented @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Order(value = Ordered.LOWEST_PRECEDENCE - 1) public @interface RedisLock {

    /

    • 前缀 *
    • @return */ String prefix() default "";

      /

    • 锁名称 *
    • @return */ String name() default "";

      /

    • 加锁等待时间 *
    • @return */ long lockWait() default 50L;
/**
 * 解锁时间
 *
 * @return
 */
long autoUnlockTime() default 2000L;

/**
 * 重试加锁次数
 *
 * @return
 */
int retryLockTimes() default 3;

/**
 * 重试加锁间隔时间
 *
 * @return
 */
long retryWait() default 50L;

} ```

  1. RedisLockAspect.java
package com.bf.form.common.aspect;

import cn.hutool.core.lang.Assert;
import com.bf.form.common.excptions.CommonException;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
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.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @Author: hyakutake
 * @Date: 2021/11/18 20:19
 * @Description:
 **/
@Slf4j
@Aspect
@Component
@Order(Ordered.LOWEST_PRECEDENCE - 1)
public class RedisLockAspect {

    private static final String LOCK_NAME = "lockName";
    private static final String LOCK_WAIT = "lockWait";
    private static final String AUTO_UNLOCK_TIME = "autoUnlockTime";
    private static final String RETRY_NUM = "retryNum";
    private static final String RETRY_WAIT = "retryWait";
    private static final String LOG_GET_LOCK = "锁{%s}:加锁成功";
    private static final String LOG_UN_LOCK = "锁{%s}:解锁成功";
    private static final String LOG_LOCK_ERROR = "加锁:{%s}锁,发生异常,异常名称:{%s}";
    private static final String LOG_ERROR_NO_NAME = "锁名不能为空";
    private static final String LOG_ERROR_NO_RETRY = "锁{%s}:已被其他线程加锁且不进行重试";
    private static final String LOG_ERROR_RETRY = "锁{%s}:已被其他线程加锁,正在重试[ %s/%s ],重试间隔{%s}毫秒";

    @Resource
    private RedissonClient redissonClient;

    @SneakyThrows
    @Around("@annotation(com.form.common.aspect.RedisLock) && @annotation(redisLock)")
    public Object eventPushAdvice(ProceedingJoinPoint proceedingJoinPoint, RedisLock redisLock) {
        // 获取注解中的参数
        Map<String, Object> annotationArgs = this.getAnnotationArgs(redisLock);
        String lockName = (String) annotationArgs.get(LOCK_NAME);
        Assert.notNull(lockName, LOG_ERROR_NO_NAME);
        int retryNum = (int) annotationArgs.get(RETRY_NUM);
        long retryWait = (long) annotationArgs.get(RETRY_WAIT);
        long lockWait = (long) annotationArgs.get(LOCK_WAIT);
        long autoUnlockTime = (long) annotationArgs.get(AUTO_UNLOCK_TIME);
        // 获取锁
        RLock lock = redissonClient.getLock(lockName);
        try {
            boolean res = lock.tryLock(lockWait, autoUnlockTime, TimeUnit.SECONDS);
            if (res) {
                // 执行主逻辑
                log.info(String.format(LOG_GET_LOCK, lockName));
                return proceedingJoinPoint.proceed();
            } else {
                // 如果重试次数为零, 则不重试
                if (retryNum <= 0) {
                    log.info(String.format(LOG_ERROR_NO_RETRY, lockName));
                    throw new CommonException(-1, String.format(LOG_ERROR_NO_RETRY, lockName));
                }
                if (retryWait == 0) {
                    retryWait = 200L;
                }
                // 设置失败次数计数器, 当到达指定次数时, 返回失败
                int failCount = 1;
                while (failCount <= retryNum) {
                    // 等待指定时间ms
                    Thread.sleep(retryWait);
                    if (lock.tryLock(lockWait, autoUnlockTime, TimeUnit.SECONDS)) {
                        // 执行主逻辑
                        return proceedingJoinPoint.proceed();
                    } else {
                        log.info(String.format(LOG_ERROR_RETRY, lockName, failCount, retryNum, retryWait));
                        failCount++;
                    }
                }
                throw new CommonException(-1, LOG_ERROR_NO_RETRY);
            }
        } catch (Throwable throwable) {
            log.error(String.format(LOG_LOCK_ERROR, lockName, throwable.getMessage()), throwable);
            throw throwable;
        } finally {
            lock.unlock();
            log.info(String.format(LOG_UN_LOCK, lockName));
        }

    }

    /**
     * 获取参数
     *
     * @return
     */
    private Map<String, Object> getAnnotationArgs(RedisLock redisLock) {
        Map<String, Object> result = new HashMap<>();
        result.put(LOCK_NAME, redisLock.prefix() + redisLock.name());
        result.put(LOCK_WAIT, redisLock.lockWait());
        result.put(AUTO_UNLOCK_TIME, redisLock.autoUnlockTime());
        result.put(RETRY_NUM, redisLock.retryLockTimes());
        result.put(RETRY_WAIT, redisLock.retryWait());
        return result;
    }
}
  1. 使用
@RedisLock(prefix = "DataCodeRecord", name = "getNewCodeLock", retryLockTimes = 50)
    public Object method(Object param) {
       //TODO: your code
   }
  1. 引用
org.redisson:redisson:3.17.1
  • Loading...
  • Loading...
  • Loading...
  • Loading...
  • Loading...