Spring boot 定时任务Scheduled集群部署下,重复执行解决方案

利用redis分布式锁实现

1.声明注解

package com.rk.iam.sys.job.redis;


import java.lang.annotation.*;
import java.util.concurrent.TimeUnit;
/**
 * @author wdy
 * @version 1.0
 * @date 2022/3/30 15:06
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
public @interface RedisLock {
    String lockPrefix() default "";
    String lockKey() default "";
    long timeOut() default 30;
    TimeUnit timeUnit() default TimeUnit.SECONDS;
}

2.注解实现

package com.rk.iam.sys.job.redis;


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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * @ClassName wdy
 * @Author lvhoushuai(tsxylhs @ outlook.com)
 * @Date 2022-03-25
 * @Description 拦截器实现
 **/
@Aspect
@Component
public class RedisLockAspect {
    private static final Integer Max_RETRY_COUNT = 3;
    private static final String LOCK_PRE_FIX = "lockPreFix";
    private static final String LOCK_KEY = "lockKey";
    private static final String TIME_OUT = "timeOut";
    private static final int PROTECT_TIME = 2 << 11;//4096
    private static final Logger log = LoggerFactory.getLogger(RedisLock.class);
    @Autowired
    private CommonRedistHelper commonRedistHelper;

    @Pointcut("@annotation(com.rk.iam.sys.job.redis.RedisLock)")
    public void redisLockAspect() {
    }

    @Around("redisLockAspect()")
    public void lockAroundAction(ProceedingJoinPoint proceeding) throws Exception {

        //获取redis锁
        Boolean flag = this.getLock(proceeding, 0, System.currentTimeMillis());
        if (flag) {
            try {
                proceeding.proceed();
                Thread.sleep(PROTECT_TIME);
            } catch (Throwable throwable) {
                throw new RuntimeException("分布式锁执行发生异常" + throwable.getMessage(), throwable);
            } finally {        // 删除锁
                this.delLock(proceeding);
            }
        } else {
            log.info("其他系统正在执行此项任务");
        }
    }


    //获取锁
    private boolean getLock(ProceedingJoinPoint proceeding, int count, long currentTime) {
        //获取注解中的参数
        Map annotationArgs = this.getAnnotationArgs(proceeding);
        String lockPrefix = (String) annotationArgs.get(LOCK_PRE_FIX);
        String key = (String) annotationArgs.get(LOCK_KEY);
        long expire = (long) annotationArgs.get(TIME_OUT);
        if (StringUtils.isEmpty(lockPrefix) || StringUtils.isEmpty(key)) {
            throw new RuntimeException("RedisLock,锁前缀,锁名未设置");
        }
        if (commonRedistHelper.setNx(lockPrefix, key, expire)) {
            return true;
        } else {
            //如果当前时间与锁的时间差,大于保护时间,则强制删除锁(防止死锁)
            long createTime = commonRedistHelper.getLockValue(lockPrefix, key);
            if ((currentTime - createTime) > (expire * 1000 + PROTECT_TIME)) {
                count++;
                if (count > Max_RETRY_COUNT) {
                    return false;

                }
                commonRedistHelper.delete(lockPrefix, key);
                getLock(proceeding, count, currentTime);
            }
            return false;
        }

    }

    /**
     * 删除锁
     */
    private void delLock(ProceedingJoinPoint proceeding) {
        Map annotationArgs = this.getAnnotationArgs(proceeding);
        String lockPrefix = (String) annotationArgs.get(LOCK_PRE_FIX);
        String key = (String) annotationArgs.get(LOCK_KEY);
        commonRedistHelper.delete(lockPrefix, key);
    }

    /**
     * 获取锁参数
     *
     * @param proceeding
     * @return
     */
    private Map getAnnotationArgs(ProceedingJoinPoint proceeding) {
        Class target = proceeding.getTarget().getClass();
        Method[] methods = target.getMethods();
        String methodName = proceeding.getSignature().getName();
        for (Method method : methods) {
            if (method.getName().equals(methodName)) {
                Map result = new HashMap();
                RedisLock redisLock = method.getAnnotation(RedisLock.class);
                result.put(LOCK_PRE_FIX, redisLock.lockPrefix());
                result.put(LOCK_KEY, redisLock.lockKey());
                result.put(TIME_OUT, redisLock.timeUnit().toSeconds(redisLock.timeOut()));
                return result;
            }
        }
        return null;
    }


}

3.redis操作

package com.rk.iam.sys.job.redis;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

/**
 * @author wdy
 * @version 1.0
 * @date 2022/3/30 15:08
 */
@Component
public class CommonRedistHelper {

    @Autowired
    RedisTemplate redisTemplate;
    /**
     * 添加分布式锁
     *
     */
    public boolean setNx(String track,String sector,long timeout){
        ValueOperations valueOperations=redisTemplate.opsForValue();
        Boolean flag=valueOperations.setIfAbsent(track+sector,System.currentTimeMillis());
        if (flag){
            valueOperations.set(track+sector,getLockValue(track,sector),timeout, TimeUnit.SECONDS);
        }
        return flag;
    }
    /**
     * 删除锁
     */
    public void delete(String track,String sector){
        redisTemplate.delete(track+sector);
    }
    /**
     * 查询锁
     * @return 写锁时间
     */
    public long getLockValue(String track, String sector) {
        ValueOperations valueOperations = redisTemplate.opsForValue();
        long createTime = (long) valueOperations.get(track + sector);
        return createTime;
    }



}

4.定时任务实现类

package com.rk.iam.sys.job;

import com.rk.iam.sys.common.enums.PushEnums;
import com.rk.iam.sys.common.vo.SysVersionVO;
import com.rk.iam.sys.job.redis.RedisLock;
import com.rk.iam.sys.service.SysProjectService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;

/**
 * @author wdy
 * @version 1.0
 * @date 2022/3/30 17:01
 */
@Configuration
@EnableScheduling
public class SysProjectJob {
    @Autowired
    private SysProjectService sysProjectService;
    @Scheduled(cron = "0 0 0 * * ?")
    @RedisLock(lockPrefix = "iamCache:job", lockKey = "SysProjectJob",timeOut = 60)
    public void sync() {

        try {
            sysProjectService.sysnProject();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

你可能感兴趣的:(spring)