redis distributed lock solves the problem of repeated execution of cluster server's scheduled tasks

Problem description

It is no problem to deploy the project with scheduled tasks on a single test environment. In production, there are two cluster servers. When the project is deployed, it is found that the module of timing task is executed on both machines at the same time, which will lead to other accidents.

Solution - redis distributed lock

The redis distributed lock is used to lock the unique key specified for the scheduled task and set the lock timeout. When a timed task is triggered, the task of a service enters the aspect, and locks the unique key through the setNX(key,value) method. If the current key does not exist, it will be put into the cache and return true. The lock timeout is set through expire(key,second). After that, the timed task method is executed. When the second service task enters, when setting the lock, it is found that the lock already exists in the cache, and returns false. It does not jump to the execute scheduled task method.

Core code

1. Distributed lock facet

@Aspect
@Slf4j
@Component
public class CacheLockAspect {

    private static final String LOCK_VALUE = "locked";

    @Autowired
    private RedisConnection connection;

    @Around("execution(* *.*(..)) && @annotation(com.common.annotation.CacheLock)")
    public void  cacheLockPoint(ProceedingJoinPoint pjp) {
        Method cacheMethod = null;
        for (Method method : pjp.getTarget().getClass().getMethods()) {
            if (null!=method.getAnnotation(CacheLock.class)){
                cacheMethod = method;
                break;
            }
        }
        try {
            String lockKey = cacheMethod.getAnnotation(CacheLock.class).lockedPrefix();
            long timeOut = cacheMethod.getAnnotation(CacheLock.class).expireTime();
            if(null == lockKey){
                throw new ManagerException(ErrorMsgEnum.LOCK_NAME_EMPTY);
            }
            if (connection.setNX(lockKey.getBytes(),LOCK_VALUE.getBytes())) {
                connection.expire(lockKey.getBytes(),timeOut);
                log.info("method:{}Get lock:{},Start running!",cacheMethod,lockKey);
                pjp.proceed();
                return;
            }
            log.info("method:{}Not acquired lock:{},Failed to run!",cacheMethod,lockKey);
        } catch (Throwable e) {
            log.error("method:{},Running error!",cacheMethod,e);
            throw new ManagerException(ErrorMsgEnum.LOCK_JOB_ERROR,e);
        }

    }
}

2. Handwritten method level notes

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CacheLock {
    String lockedPrefix() default "";   //Prefix of redis lock key
    long expireTime() default 10;      //Time of key in redis, 1000S
}

3. Scheduled task service

@Slf4j
@Service
public class TimeTaskService {

    /**
     * Perform scheduled tasks
     **/
    @Scheduled(cron = "0 0 1 * * ?")
    @CacheLock(lockedPrefix = "TimeTaskService",expireTime=30)
    public void executeTask() {
        System.out.println("hello world!");
    }

}

Keywords: Java Redis

Added by ~J~R~R on Sat, 07 Dec 2019 03:36:56 +0200