Java使用redission分布式锁--注解方式

一、需求

之前写redission分布式锁的时候都是嵌入到程序中,现在把分布式锁用注解的方式去实现,简单整理一下

二、pom依赖


        <!-- redission -->
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>3.15.5</version>
        </dependency>

三、定义注解


package com.example.redis;

import java.lang.annotation.*;
import java.util.concurrent.TimeUnit;

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

    /**
     * redis key
     *
     * @return
     */
    String key() default "";

    /**
     * 是否开启开门狗 开启看门狗后leaseTime默认为30秒,也可自己配置时间
     * 开启看门狗后,只要占锁成功,就会启动一个定时任务【重新给锁设置过期时间,新的过期时间就是看门狗的默认时间】,每隔10秒都会自动再续成30秒
     *
     * @return
     */
    boolean watchDog() default false;

    /**
     * 重试时间(重试获取锁自旋时间)
     *
     * @return
     */
    int waitTime() default 3;

    /**
     * 锁的有效时间
     *
     * @return
     */
    int leaseTime() default 3;

    /**
     * 方法结束是否要释放锁 true-方法执行结束释放锁 false-执行结束不释放锁,等待锁过期释放
     *
     * @return
     */
    boolean methodOverUnLock() default true;

    /**
     * 时间单位 默认是:秒
     *
     * @return
     */
    TimeUnit timeUnit() default TimeUnit.SECONDS;
}

四、编写切面

package com.example.redis;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

/**
 * @Author:
 * @Description redission分布式锁
 * @Date: 下午5:25 2023/4/25
 */
@Slf4j
@Aspect
@Component
public class RedisLockAspect {

    @Resource
    private RedissonClient redissonClient;

    @Pointcut("@annotation(com.example.redis.RedisLock)")
    public void commitPointCut() {
    }

    @Around("commitPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        return repeatCommit(point);
    }

    private Object repeatCommit(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("*********Redission分布式锁开始执行*********");
        // 获取request对象
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
        String ip = getIp(request);
        // 获取方法签名
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        //获取方法
        Method method = signature.getMethod();
        // 获取注解
        RedisLock commit = method.getAnnotation(RedisLock.class);
        //redis key
        String redisKey = commit.key();
        //时间单位
        TimeUnit timeUnit = commit.timeUnit();
        //是否开启看门狗
        boolean watchDog = commit.watchDog();
        //方法结束是否要释放锁
        boolean methodOverUnLock = commit.methodOverUnLock();
        //获取锁重试时间
        int waitTime = commit.waitTime();
        //锁的有效时间
        int leaseTime = commit.leaseTime();
        log.info("*********Redission分布式锁各个参数*********redisKey={},waitTime={},leaseTime={},timeUnit={},watchDog={},methodOverUnLock={}", redisKey, waitTime, leaseTime, timeUnit, watchDog, methodOverUnLock);
        if (StringUtils.isBlank(redisKey)) {
            redisKey = getRedisKey(ip, method);
        }
        try {
            //获取重入锁对象
            RLock lock = redissonClient.getLock(redisKey);
            //加锁 如果要开启"看门狗"机制,则leaseTime赋值为"-1"
            boolean locked = lock.tryLock(waitTime, true == watchDog ? -1 : leaseTime, timeUnit);
            if (!locked) {
                log.error("请勿重复提交");
                //补充自己获取锁失败的业务逻辑
            }
            // 继续执行方法
            Object proceed = joinPoint.proceed();
            log.info("*********Redission分布式锁方法执行结束*********redisKey={},waitTime={},leaseTime={},timeUnit={},watchDog={},methodOverUnLock={}", redisKey, waitTime, leaseTime, timeUnit, watchDog, methodOverUnLock);
            return proceed;
        } catch (InterruptedException e) {
            log.error("*********Redission分布式锁方法执行异常*********redisKey={},waitTime={},leaseTime={},timeUnit={},watchDog={},methodOverUnLock={}", redisKey, waitTime, leaseTime, timeUnit, watchDog, methodOverUnLock);
            log.error("*********Redission分布式锁方法执行异常*********e={}", e);
            Thread.currentThread().interrupt();
            throw e;
        } finally {
            if (methodOverUnLock) {
                unLock(redisKey);
                log.info("*********Redission分布式锁释放锁成功*********redisKey={},waitTime={},leaseTime={},timeUnit={},watchDog={},methodOverUnLock={}", redisKey, waitTime, leaseTime, timeUnit, watchDog, methodOverUnLock);
            }
        }
    }

    /**
     * 如果用户不指定key 则默认获取用户ip+类名+方法名
     *
     * @param ip
     * @param method
     * @return
     */
    private String getRedisKey(String ip, Method method) {
        String className = method.getDeclaringClass().getName();
        String methodName = method.getName();
        return String.format("%s#%s", ip + className, methodName);
    }

    /**
     * 释放锁
     *
     * @param lockKey 锁的值
     */
    public boolean unLock(String lockKey) {
        try {
            RLock lock = redissonClient.getLock(lockKey);
            if (null != lock && lock.isHeldByCurrentThread()) { //判断锁是否存在,并且判断是否当前线程加的锁。
                lock.unlock();
                return true;
            }
        } catch (Exception e) {
            log.error(String.format("释放锁%s异常", lockKey));
        }
        return false;
    }

    /**
     * 获取ip
     *
     * @param request
     * @return
     */
    public static String getIp(HttpServletRequest request) {
        String unknown = "unknown";
        if (request == null) {
            return unknown;
        }

        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }

        if (ip == null || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {
            ip = request.getHeader("X-Forwarded-For");
        }

        if (ip == null || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }

        if (ip == null || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {
            ip = request.getHeader("X-Real-IP");
        }

        if (ip == null || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }

        return ip;
    }
}

五、测试类

package com.example.service;

import com.example.redis.RedisLock;
import org.springframework.stereotype.Service;

/**
 * @Author: han tao
 * @Description
 * @Date: 下午5:34 2023/4/25
 */
@Service
public class RedisLockService {

    @RedisLock(key = "LOCK_TEST:", watchDog = true, methodOverUnLock = true)
    public void testRedisLock() {
        try {
            Thread.sleep(20000);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}


六、接口类

package com.example.api;

import com.example.service.RedisLockService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Author:
 * @Description
 * @Date: 下午5:32 2023/4/25
 */
@RestController
@RequestMapping("/redis-lock")
public class RedisLockApi {


    @Autowired
    private RedisLockService redisLockService;

    /**
     * 直接输出到前端
     *
     * @param
     */
    @GetMapping("/test")
    public void testRedisLock() {

        redisLockService.testRedisLock();

    }

}


七、结果

启动项目后,浏览器重复访问:http://localhost:8080/redis-lock/test
查看后台日志即可

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值