1. Simple implementation of Spring scheduled tasks

To use Scheduled tasks in Spring Boot, just use @enablesCheduling to enable Scheduled tasks and add @scheduled annotations to the methods that need to be Scheduled. In this way, the scheduled scheduling function can be enabled in the project, and the execution period and frequency can be flexibly controlled by cron, fixedRate, fixedDelay, etc.

1.1 disadvantages

  • Once the period is specified, you must restart the application to change it

1.2 requirements

  • Hot update periodic task execution cycle, based on CRon expression and support for external storage, such as database, NACOS, etc
  • Minimal modification compatible with existing scheduled tasks (just add an annotation)
  • Dynamically add scheduled tasks

2.Spring timing task source analysis

2.1 @EnableScheduling introduces SchedulingConfiguration

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(SchedulingConfiguration.class)
@Documented
public @interface EnableScheduling {
}
Copy the code

2.2 SchedulingConfiguration configuration only a bean, from name ScheduledAnnotationBeanPostProcessor knew the BeanPostProcessor interface

@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class SchedulingConfiguration {
   @Bean(name = TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME)
   @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
   public ScheduledAnnotationBeanPostProcessor scheduledAnnotationProcessor() {
      return new ScheduledAnnotationBeanPostProcessor();
   }
}
Copy the code

2.3 ScheduledAnnotationBeanPostProcessor postProcessAfterInitialization implementation, specific processing visible @ is Scheduled to achieve timing task processScheduled method

@Override public Object postProcessAfterInitialization(Object bean, String beanName) { if (bean instanceof AopInfrastructureBean || bean instanceof TaskScheduler || bean instanceof ScheduledExecutorService) { // Ignore AOP infrastructure such as scoped proxies. return bean; } Class<? > targetClass = AopProxyUtils.ultimateTargetClass(bean); if (! this.nonAnnotatedClasses.contains(targetClass) && AnnotationUtils.isCandidateClass(targetClass, Array.aslist (Scheduled. Class, Schedules. Class)) {Scheduled <Method, Set<Scheduled>> annotatedMethods = MethodIntrospector.selectMethods(targetClass, (MethodIntrospector.MetadataLookup<Set<Scheduled>>) method -> { Set<Scheduled> scheduledMethods = AnnotatedElementUtils.getMergedRepeatableAnnotations( method, Scheduled.class, Schedules.class); return (! scheduledMethods.isEmpty() ? scheduledMethods : null); }); if (annotatedMethods.isEmpty()) { this.nonAnnotatedClasses.add(targetClass); if (logger.isTraceEnabled()) { logger.trace("No @Scheduled annotations found on bean class: " + targetClass); } } else { // Non-empty set of methods annotatedMethods.forEach((method, ScheduledMethods) -> @scheduled annotations scheduledMethods. ForEach (Scheduled -> processScheduled(Scheduled, method)) bean))); if (logger.isTraceEnabled()) { logger.trace(annotatedMethods.size() + " @Scheduled methods processed on bean '" + beanName + "': " + annotatedMethods); } } } return bean; }Copy the code

2.4 only posted below ScheduledAnnotationBeanPostProcessor. ProcessScheduled key implementation process cron expressions,

private final ScheduledTaskRegistrar registrar; public ScheduledAnnotationBeanPostProcessor() { this.registrar = new ScheduledTaskRegistrar(); } protected void processScheduled(Scheduled Scheduled, Method Method, Object bean) {try { Runnable Runnable Runnable = createRunnable(bean, method); boolean processedSchedule = false; Set<ScheduledTask> tasks = new LinkedHashSet<>(4); Initial delay (); // Initial delay (); // Initial delay (); // Check cron expression String cron = scheduled.cron(); if (StringUtils.hasText(cron)) { String zone = scheduled.zone(); if (this.embeddedValueResolver ! = null) {/ / ${} variable conversion value expressions cron = this. EmbeddedValueResolver. ResolveStringValue (cron); zone = this.embeddedValueResolver.resolveStringValue(zone); } if (StringUtils.hasLength(cron)) { Assert.isTrue(initialDelay == -1, "'initialDelay' not supported for cron triggers"); processedSchedule = true; if (! Scheduled.CRON_DISABLED.equals(cron)) { TimeZone timeZone; if (StringUtils.hasText(zone)) { timeZone = StringUtils.parseTimeZoneString(zone); } else { timeZone = TimeZone.getDefault(); } // Create cron trigger CronTrigger object, And register CronTask tasks. The add (this) the registrar) scheduleCronTask (new CronTask (runnable, new CronTrigger (cron, timeZone)))); }}} // Handle fixedDelay and fixedRate, and ScheduledTask save for destruction, skip... } // Skip catch Exception... }Copy the code

The above by this. The registrar. ScheduleCronTask implementation cron task initialization register or regularly

3. Implement dynamic scheduled tasks

Implementation idea: Rewrite ScheduledAnnotationBeanPostProcessor processScheduled method, modified processing cron part of the code, use this. The registrar. ScheduleTriggerTask registration or initialization tasks regularly

3.1 Related class diagram

classDiagram

DisposableBean <|-- DynamicCronScheduleTaskManager
EnvironmentAware <|-- EnvironmentDynamicCronHandler
AbstractDynamicCronHandler <|-- EnvironmentDynamicCronHandler
Trigger <|-- DynamicCronTrigger

EnvironmentAware: +setEnvironment()
DisposableBean: +destroy() void
Trigger: +nextExecutionTime(TriggerContext triggerContext) Date

class DynamicCronScheduleTaskManager{
 +Map<String, ScheduledTask> dynamicScheduledTaskMap
 -ScheduledTaskRegistrar registrar
 +addTriggerTask(String cronName, TriggerTask task) ScheduledTask
 +contains(String cronName) boolean
 +updateTriggerTask(String cronName) void
 +removeTriggerTask(String cronName) void
}

class AbstractDynamicCronHandler{
 -DynamicCronScheduleTaskManager dynamicCronScheduleTaskManager;
 +getCronExpression(String cronName) String
 +updateTriggerTash(String cronName) void
}

class EnvironmentDynamicCronHandler{
+Environment environment
+environmentChangeEvent(EnvironmentChangeEvent event) void
}
class DynamicCronTrigger{
-String cronName
-AbstractDynamicCronHandler dynamicCronHandler
-String cronExpression
-CronSequenceGenerator sequenceGenerator
}

class ScheduledDynamicCron{
 +value() String
 +cronName() String
 +handler() Class<? extends AbstractDynamicCronHandler>
}

3.2 DynamicCronScheduleTaskManager

import org.springframework.beans.factory.DisposableBean; import org.springframework.scheduling.config.ScheduledTask; import org.springframework.scheduling.config.ScheduledTaskRegistrar; import org.springframework.scheduling.config.TriggerTask; import java.util.HashMap; import java.util.Map; / * * * @ author HuangJS * * @ the date 2021-12-11 3:04 p.m afternoon/public class DynamicCronScheduleTaskManager implements DisposableBean { private Map<String, ScheduledTask> dynamicScheduledTaskMap = new HashMap<>(); ScheduledTaskRegistrar registrar; Public ScheduledTask addTriggerTask(String cronName, TriggerTask task) { ScheduledTask scheduledTask = dynamicScheduledTaskMap.get(cronName); if (scheduledTask ! = null) { scheduledTask.cancel(); } scheduledTask = this.registrar.scheduleTriggerTask(task); dynamicScheduledTaskMap.put(cronName, scheduledTask); return scheduledTask; } public boolean contains(String cronName){ return this.dynamicScheduledTaskMap.containsKey(cronName); } // Timing of updating scheduled tasks public void updateTriggerTask(String cronName) {ScheduledTask ScheduledTask = dynamicScheduledTaskMap.get(cronName); if (scheduledTask == null) { throw new IllegalStateException("Invalid cronName "" + cronName + "",no fund ScheduledTask"); } scheduledTask.cancel(); scheduledTask = this.registrar.scheduleTriggerTask((TriggerTask) scheduledTask.getTask()); dynamicScheduledTaskMap.put(cronName, scheduledTask); } // remove ScheduledTask public void removeTriggerTask(String cronName) {ScheduledTask ScheduledTask = dynamicScheduledTaskMap.remove(cronName); if (scheduledTask ! = null) { scheduledTask.cancel(); } } @Override public void destroy() throws Exception { for (ScheduledTask value : dynamicScheduledTaskMap.values()) { value.cancel(); } this.dynamicScheduledTaskMap.clear(); }}Copy the code

3.3 AbstractDynamicCronHandler

public abstract class AbstractDynamicCronHandler { @Autowired protected DynamicCronScheduleTaskManager dynamicCronScheduleTaskManager; /** * getCronExpression * @return */ public abstract String getCronExpression(String cronName); @param cronName */ public void updateTriggerTask(String cronName) {public void updateTriggerTask(String cronName) { dynamicCronScheduleTaskManager.updateTriggerTask(cronName); }}Copy the code

3.4 EnvironmentDynamicCronHandler

Based on Environment, the system automatically refreshes the trigger time of scheduled tasks when refreshing configurations, supporting distributed multi-node cluster deployment.

For example, cron expressions are configured on NACOS, and updating the configuration on NACOS is implemented by listening for EnvironmentChangeEvent events

import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.context.environment.EnvironmentChangeEvent; import org.springframework.context.EnvironmentAware; import org.springframework.context.event.EventListener; import org.springframework.core.env.Environment; / * * * @ author HuangJS * * @ the date 2021-12-11 11:46 morning/public class EnvironmentDynamicCronHandler extends AbstractDynamicCronHandler implements EnvironmentAware { private final Logger logger = LoggerFactory.getLogger(EnvironmentDynamicCronHandler.class); private Environment environment; @Override public String getCronExpression(String cronName) { try { return environment.getProperty(cronName); } catch (Exception e) { logger.error(e.getMessage(), e); } return null; } @Override public void setEnvironment(Environment environment) { this.environment = environment; } @EventListener public void environmentChangeEvent(EnvironmentChangeEvent event) { for (String key : event.getKeys()) { if (this.dynamicCronScheduleTaskManager.contains(key)) { this.dynamicCronScheduleTaskManager.updateTriggerTask(key); }}}}Copy the code

3.5 DynamicCronTrigger

public class DynamicCronTrigger implements Trigger { private final static Logger LOGGER = LoggerFactory.getLogger(DynamicCronTrigger.class); private String cronName; private AbstractDynamicCronHandler dynamicCronHandler; private String cronExpression; private CronSequenceGenerator sequenceGenerator; public DynamicCronTrigger(String cronName, AbstractDynamicCronHandler dynamicCronHandler) { this.cronName = cronName; this.dynamicCronHandler = dynamicCronHandler; } @Override public Date nextExecutionTime(TriggerContext triggerContext) { String cronExpression = dynamicCronHandler.getCronExpression(cronName); if (cronExpression == null) { return null; } if (this.sequenceGenerator == null || ! cronExpression.equals(this.cronExpression)) { try { this.sequenceGenerator = new CronSequenceGenerator(cronExpression); this.cronExpression = cronExpression; } catch (Exception e) { LOGGER.error(e.getMessage(), e); } } Date date = triggerContext.lastCompletionTime(); if (date ! = null) { Date scheduled = triggerContext.lastScheduledExecutionTime(); if (scheduled ! = null && date.before(scheduled)) { // Previous task apparently executed too early... // Let's simply use the last calculated execution time then, // in order to prevent accidental re-fires in the same second. date = scheduled; } } else { date = new Date(); } return this.sequenceGenerator.next(date); }}Copy the code

3.6 Annotation class ScheduledDynamicCron

@Target({ElementType.METHOD,ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface ScheduledDynamicCron {/** * dynamic cron name * @return */ @aliasfor ("cronName") String value() default ""; /** * Dynamic cron name * @return */ @aliASfor ("value") String cronName() default ""; /** * dynamic cron processing Class, default to use the Environment based processing Class * @return */ Class<? extends AbstractDynamicCronHandler> handler() default EnvironmentDynamicCronHandler.class; }Copy the code

3.7 DynamicScheduledAnnotationBeanPostProcessor

import org.springframework.beans.factory.BeanFactory;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor;
import org.springframework.scheduling.config.*;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.util.StringValueResolver;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.time.Duration;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;

/**
 *
 * @author HuangJS
 * @date 2020/5/14 17:23
 **/

public class DynamicScheduledAnnotationBeanPostProcessor extends ScheduledAnnotationBeanPostProcessor {
    private StringValueResolver embeddedValueResolver;
    private BeanFactory beanFactory;
    private DynamicCronScheduleTaskManager dynamicCronScheduleTaskManager;

    private final ScheduledTaskRegistrar registrar = (ScheduledTaskRegistrar) getFieldValueFromParentClass("registrar");
    private final Map<Object, Set<ScheduledTask>> scheduledTasks = (Map) getFieldValueFromParentClass("scheduledTasks");

    public DynamicScheduledAnnotationBeanPostProcessor(DynamicCronScheduleTaskManager dynamicCronScheduleTaskManager) {
        this.dynamicCronScheduleTaskManager = dynamicCronScheduleTaskManager;
        this.dynamicCronScheduleTaskManager.registrar = this.registrar;
    }

    @Override
    public void setEmbeddedValueResolver(StringValueResolver resolver) {
        super.setEmbeddedValueResolver(resolver);
        this.embeddedValueResolver = resolver;
    }
    
    @Override
    public void setBeanFactory(BeanFactory beanFactory) {
        super.setBeanFactory(beanFactory);
        this.beanFactory = beanFactory;
    }
    
    /**
     * @param scheduled
     * @param method
     * @param bean
     * @see ScheduledAnnotationBeanPostProcessor
     */
    @Override
    protected void processScheduled(Scheduled scheduled, Method method, Object bean) {
        try {
            Runnable runnable = createRunnable(bean, method);
            boolean processedSchedule = false;
            String errorMessage =
                    "Exactly one of the 'cron', 'fixedDelay(String)', or 'fixedRate(String)' attributes is required";

            Set<ScheduledTask> tasks = new LinkedHashSet<>(4);

            // Determine initial delay
            long initialDelay = scheduled.initialDelay();
            String initialDelayString = scheduled.initialDelayString();
            if (StringUtils.hasText(initialDelayString)) {
                Assert.isTrue(initialDelay < 0, "Specify 'initialDelay' or 'initialDelayString', not both");
                if (this.embeddedValueResolver != null) {
                    initialDelayString = this.embeddedValueResolver.resolveStringValue(initialDelayString);
                }
                if (StringUtils.hasLength(initialDelayString)) {
                    try {
                        initialDelay = parseDelayAsLong(initialDelayString);
                    }
                    catch (RuntimeException ex) {
                        throw new IllegalArgumentException(
                                "Invalid initialDelayString value "" + initialDelayString + "" - cannot parse into long");
                    }
                }
            }
    
            // 处理ScheduledDynamicCron注解
            if (method.isAnnotationPresent(ScheduledDynamicCron.class)) {
                ScheduledDynamicCron dynamicCron = method.getAnnotation(ScheduledDynamicCron.class);
                String cronName = dynamicCron.value();
                if (StringUtils.hasText(dynamicCron.cronName())) {
                    cronName = dynamicCron.cronName();
                } else {
                    Assert.isTrue(StringUtils.hasText(cronName), "@ScheduledDynamicCron 'cronName' or 'value' attributes is required");
                }
                
                DynamicCronTrigger trigger = new DynamicCronTrigger(cronName, this.beanFactory.getBean(dynamicCron.handler()));
                ScheduledTask scheduledTask = this.dynamicCronScheduleTaskManager.addTriggerTask(cronName, new TriggerTask(runnable, trigger));
                tasks.add(scheduledTask);
    
                processedSchedule = true;
            } else {
                // Check cron expression
                String cron = scheduled.cron();
                if (StringUtils.hasText(cron)) {
                    String zone = scheduled.zone();
                    if (this.embeddedValueResolver != null) {
                        cron = this.embeddedValueResolver.resolveStringValue(cron);
                        zone = this.embeddedValueResolver.resolveStringValue(zone);
                    }
                    if (StringUtils.hasLength(cron)) {
                        Assert.isTrue(initialDelay == -1, "'initialDelay' not supported for cron triggers");
                        processedSchedule = true;
                        if (!Scheduled.CRON_DISABLED.equals(cron)) {
                            TimeZone timeZone;
                            if (StringUtils.hasText(zone)) {
                                timeZone = StringUtils.parseTimeZoneString(zone);
                            }
                            else {
                                timeZone = TimeZone.getDefault();
                            }
                            tasks.add(this.registrar.scheduleCronTask(new CronTask(runnable, new CronTrigger(cron, timeZone))));
                        }
                    }
                }
            }

            // At this point we don't need to differentiate between initial delay set or not anymore
            if (initialDelay < 0) {
                initialDelay = 0;
            }

            // Check fixed delay
            long fixedDelay = scheduled.fixedDelay();
            if (fixedDelay >= 0) {
                Assert.isTrue(!processedSchedule, errorMessage);
                processedSchedule = true;
                tasks.add(this.registrar.scheduleFixedDelayTask(new FixedDelayTask(runnable, fixedDelay, initialDelay)));
            }
            String fixedDelayString = scheduled.fixedDelayString();
            if (StringUtils.hasText(fixedDelayString)) {
                if (this.embeddedValueResolver != null) {
                    fixedDelayString = this.embeddedValueResolver.resolveStringValue(fixedDelayString);
                }
                if (StringUtils.hasLength(fixedDelayString)) {
                    Assert.isTrue(!processedSchedule, errorMessage);
                    processedSchedule = true;
                    try {
                        fixedDelay = parseDelayAsLong(fixedDelayString);
                    }
                    catch (RuntimeException ex) {
                        throw new IllegalArgumentException(
                                "Invalid fixedDelayString value "" + fixedDelayString + "" - cannot parse into long");
                    }
                    tasks.add(this.registrar.scheduleFixedDelayTask(new FixedDelayTask(runnable, fixedDelay, initialDelay)));
                }
            }

            // Check fixed rate
            long fixedRate = scheduled.fixedRate();
            if (fixedRate >= 0) {
                Assert.isTrue(!processedSchedule, errorMessage);
                processedSchedule = true;
                tasks.add(this.registrar.scheduleFixedRateTask(new FixedRateTask(runnable, fixedRate, initialDelay)));
            }
            String fixedRateString = scheduled.fixedRateString();
            if (StringUtils.hasText(fixedRateString)) {
                if (this.embeddedValueResolver != null) {
                    fixedRateString = this.embeddedValueResolver.resolveStringValue(fixedRateString);
                }
                if (StringUtils.hasLength(fixedRateString)) {
                    Assert.isTrue(!processedSchedule, errorMessage);
                    processedSchedule = true;
                    try {
                        fixedRate = parseDelayAsLong(fixedRateString);
                    }
                    catch (RuntimeException ex) {
                        throw new IllegalArgumentException(
                                "Invalid fixedRateString value "" + fixedRateString + "" - cannot parse into long");
                    }
                    tasks.add(this.registrar.scheduleFixedRateTask(new FixedRateTask(runnable, fixedRate, initialDelay)));
                }
            }

            // Check whether we had any attribute set
            Assert.isTrue(processedSchedule, errorMessage);

            // Finally register the scheduled tasks
            synchronized (this.scheduledTasks) {
                Set<ScheduledTask> regTasks = this.scheduledTasks.computeIfAbsent(bean, key -> new LinkedHashSet<>(4));
                regTasks.addAll(tasks);
            }
        }
        catch (IllegalArgumentException ex) {
            throw new IllegalStateException(
                    "Encountered invalid @Scheduled method '" + method.getName() + "': " + ex.getMessage());
        }
    }
    private Object getFieldValueFromParentClass(String registrar) {
        try {
            Field field = ScheduledAnnotationBeanPostProcessor.class.getDeclaredField(registrar);
            field.setAccessible(true);
            Object fieldVaule = field.get(this);
            return fieldVaule;
        } catch (Exception e) {
            logger.error("通过反射读取ScheduledAnnotationBeanPostProcessor.{} 的时候出现错误", e);
            throw new IllegalArgumentException(e);
        }
    }
    private static long parseDelayAsLong(String value) throws RuntimeException {
        return value.length() <= 1 || !isP(value.charAt(0)) && !isP(value.charAt(1)) ? Long.parseLong(value) : Duration.parse(value).toMillis();
    }
    private static boolean isP(char ch) {
        return ch == 'P' || ch == 'p';
    }
}
Copy the code

3.8 Configuring the Class SchedulerConfiguration

@enablescheduling public class SchedulerConfiguration implements SchedulingConfigurer {/** * Number of threads that execute a task */ @Value("${app.scheduler.thread.count:10}") private int schedulerThreadCount; @Override public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { TaskScheduler taskScheduler = new ConcurrentTaskScheduler(new ScheduledThreadPoolExecutor(schedulerThreadCount)); taskRegistrar.setTaskScheduler(taskScheduler); } @Bean public EnvironmentDynamicCronHandler environmentDynamicCronHandler() { return new EnvironmentDynamicCronHandler(); } @Bean public DynamicCronScheduleTaskManager dynamicCronScheduleTaskManager() { return new DynamicCronScheduleTaskManager(); } @Bean(name = TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME) @Role(BeanDefinition.ROLE_INFRASTRUCTURE) @Primary public ScheduledAnnotationBeanPostProcessor scheduledAnnotationProcessor(DynamicCronScheduleTaskManager dynamicCronScheduleTaskManager) { return new RedisScheduledAnnotationBeanPostProcessor(dynamicCronScheduleTaskManager); }}Copy the code

4. Example of Using Spring Boot

4.1 Configuration Mode: Import the configuration class from the startup classSchedulerConfiguration

The following is an example:

@ SpringBootApplication @ Import (SchedulerConfiguration. Class) / / Import configuration class public class DemoApplication {public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); }}Copy the code

4.2 NACOS Example for Configuring CRON

Nacos configuration

cron: name1: "0/5 * * * * ?" # Execute every 5 secondsCopy the code

Using the @ ScheduledDynamicCron specifies the configuration cron expression cron. Name1, do not specify a handler () default EnvironmentDynamicCronHandler, the class will be based on the specified configuration of cron. Naco name1 Cron expression on s

@Component public class DynamicTask { @Scheduled @ScheduledDynamicCron("cron.name1") public void dynamicCronForEnvironment() { System.out.println("dynamicCronForEnvironment:" + DateUtils.format(LocalDateTime.now())); }}Copy the code
  • @ScheduledIt still needs to be added, but the cron attribute configuration is ignored
  • Modify thenacosthecron.name1Configured to0/2 * * * *?The scheduled task will be executed once every 2 seconds instead of once every 5 seconds

4.3 Database Storage CRON Example

Extension AbstractDynamicCronHandler, realize from the database query cron expression

import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class DynamicCronHandler extends AbstractDynamicCronHandler { @Autowired private ParametersService parametersService; @override public String getCronExpression(String cronName) {// Query cron expression Parameters ap = saved in the database by cronName parametersService.getByName(cronName); return ap.getValue(); }}Copy the code

Scheduled Task class

@Component public class DynamicTask {@scheduled // Need to specify handler for DynamicCronHandler @scheduledDynamicCron (cronName =)  "cron.name2", handler = DynamicCronHandler.class) public void dynamicCronForDb() { System.out.println("dynamicCronForDb:" + LocalDateTime.now()); }}Copy the code

Scheduled tasks trigger timing updates, which need to be updated when the database configuration is updated

@RestController @RequestMapping("parameters") public class ParametersController { @Autowired private ParametersService parametersService; @Autowired private DynamicCronHandler dynamicCronHandler; @PostMapping("/update") public Result update(Parameters parameters){ if (parametersService.update(parameters)) { if (" cron. Name2 ". The equals (parameters. The getName ())) {/ / update the database configuration, update timer task trigger dynamicCronHandler. UpdateTriggerTask (cronName); } } return Result.success(); }}Copy the code

4.4 Updating scheduled Tasks of the Distributed Cluster Deployment Service

After the database configuration is updated, the trigger time of the synchronization update task takes effect only for the current service. Other service nodes in the cluster are not updated

Updates of other nodes can be updated by message bus, such as sending broadcast messages via MQ, and other service nodes call the following methods after consuming messages to update the task trigger timing

dynamicCronHandler.updateTriggerTask(cronName);
Copy the code

4.5 Adding a Scheduled Task

Add a Web interface for a task

@RestController @RequestMapping("dynamicCron") public class DynamicCronController { @Autowired private DynamicCronScheduleTaskManager dynamicCronScheduleTaskManager; @Autowired private EnvironmentDynamicCronHandler environmentDynamicCronHandler; @postMapping ("/addTask") public Result addTask(String cronName){// Create Runnable Runnable Runnable = () -> System.out.println("run task:" + LocalDateTime.now()); / / use EnvironmentDynamicCronHandler, Create a trigger DynamicCronTrigger trigger = new DynamicCronTrigger (cronName environmentDynamicCronHandler); / / add timing task dynamicCronScheduleTaskManager. AddTriggerTask (cronName, new TriggerTask (runnable, the trigger)); return Result.success(); }}Copy the code

When the interface is executed, scheduled tasks will not be executed because cron.name2 has not been configured. After the cron expression is configured in nacOS, scheduled tasks will be scheduled

Console output after configuring NACOS