SpringBoot Advanced Way — запланированная задача по расписанию

Spring Boot

В реальной разработке многие мелкие партнеры наверняка сталкивались с необходимостью регулярного выполнения определенной бизнес-логики, и существует множество решений, таких как MQ. Тем не менее, то, что блогер представляет здесь, — это относительно простой в использовании запланированный компонент задачи Schedule, предоставляемый springboot.

Всем известно, что spring создает временные задачи, так что есть три способа:

  • 基于注解 (@Scheduled)
  • 基于接口 (SchedulingConfigurer)
  • 基于注解设定多线程定时任务

1. На основе аннотаций (@Scheduled)

Следует отметить, что @Scheduled по умолчанию является последовательным и однопоточным.При запуске нескольких задач время выполнения задачи будет зависеть от времени выполнения предыдущей задачи.

@Configuration
@EnableScheduling    //开启定时任务
public class ScheduleTask {
    //每10秒执行一次
    @Scheduled(cron = "0/10 * * * * ?")
    private void configureTasks() {
        System.out.println("我是一个定时任务");
    }
}

@Scheduled 除了cron还提供另外三种种方式: fixedRate,fixedDelay,initialDelay1. Выражение cron можно настроить для выполнения задач, но метод выполнения похож на fixedDelay, и он начнет отсчет с момента окончания последнего метода. 2. Интервал времени между выполнением метода управления fixedDelay отсчитывается от начала выполнения предыдущего метода, если выполнение предыдущего метода заблокировано, следующее выполнение будет выполняться до завершения предыдущего выполнения и после заданный интервал времени.

@Configuration
@EnableScheduling    //开启定时任务
public class ScheduleTask {
    //每10秒执行一次
    @Scheduled(fixedDelay = 10000)
    private void configureTasks() {
        System.out.println("我是一个定时任务");
    }
}

3. FixedRate выполняется с определенной скоростью, начиная с момента выполнения последнего метода, если последний метод заблокирован, то в следующий раз он выполняться не будет, но количество раз, которое должно выполняться в период блокировки, равно Когда он перестанет быть заблокирован, выполните все сразу, а затем продолжите выполнение с фиксированной скоростью.

@Configuration
@EnableScheduling    //开启定时任务
public class ScheduleTask {
    //每10秒执行一次
    @Scheduled(fixedRate = 10000)
    private void configureTasks() {
        System.out.println("我是一个定时任务");
    }
}

4. initialDelay = 10000 означает, что таймер сработает с задержкой в ​​10 секунд после запуска контейнера.

@Configuration
@EnableScheduling    //开启定时任务
public class ScheduleTask {
    //容器启动后,延迟10秒后再执行一次定时器,以后每10秒再执行一次该定时器。
    @Scheduled(initialDelay = 10000, fixedRate = 10000)
    private void configureTasks() {
        System.out.println("我是一个定时任务");
    }
}

2. На основе интерфейса (SchedulingConfigurer)

Некоторым обезьянкам может быть удобно использовать аннотацию @Scheduled, но недостаток в том, что когда мы настраиваем цикл выполнения, нам нужно перезапустить приложение, чтобы оно вступило в силу, что несколько неудобно. Чтобы добиться эффекта вступления в силу в режиме реального времени, вы можете использовать интерфейс для выполнения задачи синхронизации.

  • Из данных времени цикла и динамического времени выполнения задачи производится выборка следующих случаев:
@Configuration      
@EnableScheduling   //开启定时任务
public class DynamicScheduleTask implements SchedulingConfigurer {

    //从数据获取任务执行周期
    @Autowired
    private MyBatisMapper myBatisMapper;

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        taskRegistrar.addTriggerTask(
                //1.添加任务内容(Runnable)
                () -> System.out.println("执行动态定时任务: " + LocalDateTime.now().toLocalTime()),
                //2.设置执行周期(Trigger)
                triggerContext -> {
                    //2.1从数据库获取执行周期
                    String cron = myBatisMapper.getCron();
                    //2.2 返回执行周期(Date)
                    return new CronTrigger(cron).nextExecutionTime(triggerContext);
                }
        );
    }
}

Данные таблицы базы данных выглядят следующим образом:

在数据库表中插入时间周期.png

Хорошо, давайте начнем тест и посмотрим

执行动态定时任务: 17:17:00.008999
执行动态定时任务: 17:17:20.002501
执行动态定时任务: 17:17:30.001786
执行动态定时任务: 17:17:40.005512
执行动态定时任务: 17:17:50.005870
执行动态定时任务: 17:18:00.002189
执行动态定时任务: 17:18:10.001910

Мы видим, что задача выполняется каждые 10 секунд. Итак, теперь требуется выполнять каждые 5 секунд, как это сделать? На данный момент нам нужно только изменить данные базы данных без перезапуска.

修改数据为5秒每次.png
Хорошо, давайте посмотрим, что напечатает консоль?

执行动态定时任务: 17:18:30.000902
执行动态定时任务: 17:18:40.001392
执行动态定时任务: 17:18:45.005027
执行动态定时任务: 17:18:50.001367
执行动态定时任务: 17:18:55.001356
执行动态定时任务: 17:19:00.001582
执行动态定时任务: 17:19:05.005676
执行动态定时任务: 17:19:10.001258
执行动态定时任务: 17:19:15.005272

Успех выполняется каждые 5 секунд. Разве не привет~

3. Настройка многопоточных задач синхронизации на основе аннотаций

Как упоминалось ранее, задача цикла исполнения @scheduled будет затронута время выполнения последней задачи. Тогда вы можете включить многопоточное выполнение периодических задач.

@EnableScheduling   // 1.开启定时任务
@EnableAsync        // 2.开启多线程
@Component
public class MultiThreadScheduleTask {

    @Async
    @Scheduled(fixedDelay = 1000)  //间隔1秒
    public void first() throws InterruptedException {
        System.out.println("第一个定时任务开始 : " + LocalDateTime.now().toLocalTime() + "\r\n线程 : " + Thread.currentThread().getName());
        Thread.sleep(1000 * 10);
    }

    @Async
    @Scheduled(fixedDelay = 2000)
    public void second() {
        System.out.println("第二个定时任务开始 : " + LocalDateTime.now().toLocalTime() + "\r\n线程 : " + Thread.currentThread().getName());
    }
}

Перезапустим проект и посмотрим, что выводит консоль:

第二个定时任务开始 : 17:27:01.024288
线程 : task-4
第一个定时任务开始 : 17:27:01.024393
线程 : task-7
第一个定时任务开始 : 17:27:02.027932
线程 : task-4
第二个定时任务开始 : 17:27:05.021294
线程 : task-1
第一个定时任务开始 : 17:27:05.021533
线程 : task-1
第一个定时任务开始 : 17:27:06.014213

Видите, поскольку многопоточность включена, время выполнения первой задачи также не ограничивается ее собственным временем выполнения. Эти две задачи также не влияют друг на друга.