Redis-based spring task cluster configuration

  The project changes from a single-node environment to a cluster environment. At this time, it is necessary to ensure that the scheduled tasks in the project can only run on one of the servers in the cluster at the same time, but cannot be written to run on which server. How can this be achieved? needs?

 

Ideas:

You can make an aspect, scan the scheduled tasks, and use the redis cache to determine whether the task is started before the task starts. Since Redis executes commands in sequence with a single thread, you can use the setnx method to determine whether a value can be added before each scheduled task is executed. The addition is successful, indicating that the scheduled task has not been executed. Set the expiration time of the key and let the scheduled task execute. If the value cannot be added, it means that the scheduled task has already run on other servers, and returns between methods. The expiration time of the key is greater than the sum of the method execution setnx in the cluster, and not greater than the time interval of the scheduled task.

 

1. Define a custom annotation to configure the key, expiration time, repository and other information of the scheduled task

package com.study.task;

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

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

    /** redis stores the corresponding key */ 
    String key() default "" ;
    
    /** The value corresponding to the key */ 
    String value() default "1" ;
    
    /** Select the first redis library to store the key */ 
    String index() default "0" ;
    
    /** The expiration time of the key */ 
    String timeout() default "30" ;
    
    /** Whether to allow multiple nodes to run concurrently */ 
    boolean isConcurrent() default  false ;
    
}

 

2. Define a slice, scan the timed task, and use redis's setnx to judge before the timed task is executed

package com.study.aop;

import java.lang.reflect.Method;

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.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;

import com.study.task.TaskRedisConfig;
import com.study.util.RedisUtil;

import redis.clients.jedis.Jedis;

@Aspect
@Component
@EnableAspectJAutoProxy
public class TaskAop {
    
    @Pointcut("@annotation(org.springframework.scheduling.annotation.Scheduled)")
    public void pointcut(){}
    
    /*@Before("pointcut()")
    public void taskBefore(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println(methodName);
        Method method = (Method)joinPoint.getThis();
        //Method method = (Method)joinPoint.getTarget();
        if(method.isAnnotationPresent(TaskRedisConfig.class)) {
            System.out.println("aaaaaaaaaa---------");
        }
    }*/
    @Around("pointcut()")
    public void taskBefore(ProceedingJoinPoint joinPoint) throws Throwable {
        String methodName = joinPoint.getSignature().getName();
        System.out.println(methodName);
        MethodSignature methodSignature = (MethodSignature)joinPoint.getSignature();   
        Method method = methodSignature.getMethod();
        if(method.isAnnotationPresent(TaskRedisConfig.class)) {
            TaskRedisConfig tc = method.getAnnotation(TaskRedisConfig.class);
            String index = tc.index();
            Jedis jedis = RedisUtil.getJedis();
            jedis.select(Integer.parseInt(index));
            String key = tc.key();
            String value = tc.value();
            String timeout = tc.timeout();
             boolean isConcurrent = tc.isConcurrent();
             if (! isConcurrent) {
                 // Set successfully, return 1. Setting failed, returns 0. 
                long result = jedis.setnx(key, value);
                 if (result==1 ) {
                     long exresult = jedis.expire(key, Integer.parseInt(timeout));
                    joinPoint.proceed();
                    jedis.close();
                }else {
                    return;
                }
            }
            
        }
    }

}

 

3. Write a timed task class for testing

package com.study.task;

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class SimpleTask {
    
    @Scheduled(cron = "0/10 * * * * ?")
    @TaskRedisConfig(key="simpleTask:methodOne",timeout="9",index="2")
    @Transactional
    public void methodOne() {
        System.out.println("methodOne----,时间:" + System.currentTimeMillis()/1000);
    }
    
    @Scheduled(cron = "0/20 * * * * ?")
    @TaskRedisConfig(key="simpleTask:methodTwo",timeout="18",index="2")
    public void methodTwo() {
        System.out.println("methodTwo----,时间:" + System.currentTimeMillis()/1000);
    }

}

 

You can put the project into two workspaces, use two eclipses to start the two projects respectively, and view the results

 

Code git address: https://gitee.com/sjcq/redis.git

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325067605&siteId=291194637