AOP注解方式实现redis分布式抢占锁

版权声明:转载请说明,谢谢 https://blog.csdn.net/qrainly/article/details/86544382

摘要

很多项目中都会有一些需要做定时跑批的任务需求,大多数是通过spring注解的方式实现的,但是到了生产环境,多节点的部署可能会造成定时任务的多节点同时触发而可能会出现脏数据。之前的处理方案是通过在字典里配置指定生产节点处理定时任务。虽然此方法也能实现需求,但总觉得很low,所以自己就通过JAVA的AOP方式利用redis实现了一套分布式抢占锁,通过注解的方式解决生产环境多节点部署带来的定时任务触发。

废话不多说,直接上代码–>
1、先自定义一个注解

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

/**
 * @author v_liuwen
 * @date 2018/12/27
 */
@Target({ElementType.METHOD,ElementType.TYPE}) // 作用到类,方法,接口上等
@Retention(RetentionPolicy.RUNTIME) // 在运行时可以获取
public @interface RedisLock {
}

2、再新建一个切面类


import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import top.qrainly.bj_demo.job.springtask.scheduleJob.utils.RedisLockUtils;

/**
 * @author v_liuwen
 * @date 2018/12/27
 */
@Aspect
@Component
public class RedisLockAspect {

    private static Logger logger = LoggerFactory.getLogger(RedisLockAspect.class);

    /**
     * 切面 加有 RedisLock 的 service 方法
     */
    @Pointcut("@annotation(redisLock)")
    private void cutMethod(RedisLock redisLock) {
    }

    @Before("cutMethod(redisLock)")
    public void doAccessCheck(JoinPoint point,RedisLock redisLock) throws NoSuchMethodException {
        logger.info("********************************【Before】开始进入AOP切入**************************************");
    }

    @After("cutMethod(redisLock)")
    public void after(JoinPoint point,RedisLock redisLock) {
        logger.info("********************************【after】AOP切入完成**************************************");
    }

    @AfterThrowing("cutMethod(redisLock)")
    public void doAfterThrow(RedisLock redisLock) {
        logger.info("AfterThrowing...");
    }


    @Around("cutMethod(redisLock)")
    public void around(ProceedingJoinPoint point,RedisLock redisLock){
        String name = point.getSignature().getName();
        Class<?> classTarget = point.getTarget().getClass();
        System.out.println("--------------------------------------->>>AOP切入 方法名:"+name+"<<<-----------------------------------------------------");
        System.out.println("--------------------------------------->>>AOP切入 类名:"+classTarget.getSimpleName()+"<<<-----------------------------------------------------");
        //获取redis锁
        Boolean lock = RedisLockUtils.acquireRedisLock(StringUtils.join(classTarget.getSimpleName(),name), 5);
        if(lock){
            try {
                point.proceed();
            } catch (Throwable throwable) {
               logger.error("AOP 代理执行失败");
            }
        }
    }


}

3、在需求做定时任务的方法上添加自定义注解@RedisLock


    /**
     * 测试批处理
     */
    @Override
    @RedisLock
    public void syncTest() {
        try{
            //获取所有需要同步状态的付款单据
            List<String> allNeedSyncStatusForPayment = batchDAO.getAllNeedSyncStatusForPayment();
            SimpleDateFormat sdf = new SimpleDateFormat();
            System.out.println("*****************************************"+sdf.format(new Date())+"开始***********************************************");
            System.out.println(JSON.toJSONString(allNeedSyncStatusForPayment));
            System.out.println("*****************************************"+sdf.format(new Date())+"结束***********************************************");
        }catch (Exception e){
            logger.error(e.getMessage());
        }
    }

4、注意事项:
4.1 注解不要放在service层 反射代理会绕过spring注解

补充RedisLockUtils

/**
 * @author v_liuwen
 * @date 2018/12/26
 */
public class RedisLockUtils {

    /**
     * 日志打印
     */
    private static Logger logger = LoggerFactory.getLogger(RedisLockUtils.class);

    private static Jedis jedis;

    /**
     * 获取jedis
     *
     * @return
     */
    public static Jedis getJedis() {
        Jedis jedis = SpringContextHolder.getBean(JedisUtils.class).getResource();
        return jedis;

    }
    /**
     * 释放jedis
     *
     * @return
     */
    public static void releaseJedis(Jedis jedis) {
        jedis.close();
    }

    /**
     * 抢占执行权 锁
     * @param lockName
     * @param lockExpire
     * @return
     */
    public static Boolean acquireRedisLock(String lockName,int lockExpire){
        Jedis jedis = getJedis();
        String identifier = UUID.randomUUID().toString();
        String lockKey = lockName;
        try{
            if (jedis.setnx(lockKey, identifier) == 1) {
                logger.info("Get lock {} success:{}.",lockName,identifier);
                jedis.expire(lockKey,lockExpire);
                if (jedis.ttl(lockKey) == -1) {
                    jedis.expire(lockKey, lockExpire);
                }
                return true;
            }
            return false;
        }catch (Exception e){
            logger.error(e.getMessage());
            return false;
        }finally {
            if(jedis !=null){
                releaseJedis(jedis);
            }
        }
    }
}

纸上谈兵终觉浅,绝知此事要躬行

猜你喜欢

转载自blog.csdn.net/qrainly/article/details/86544382
今日推荐