1. 개요

이 튜토리얼에서는 Spring @Scheduled 어노테이션 을 사용하여 작업을 구성하고 예약하는 방법을 설명합니다.

@Scheduled메서드에 어노테이션을 달기 위해 따라야하는 간단한 규칙은 다음과 같습니다.

  • 메서드는 void 반환 유형을 가져야합니다.
  • 메소드는 매개 변수를 허용하지 않아야합니다.

2. 예약 지원 활성화

Spring에서 스케줄링 작업 및 @Scheduled 어노테이션에 대한 지원을 활성화하기 위해 Java enable-style 어노테이션을 사용할 수 있습니다.

@Configuration
@EnableScheduling
public class SpringConfig {
    ...
}

반대로 XML에서도 똑같이 할 수 있습니다.

<task:annotation-driven>

3. 고정 된 지연으로 작업 예약

고정 된 지연 후에 실행되도록 작업을 구성하여 시작하겠습니다.

@Scheduled(fixedDelay = 1000)
public void scheduleFixedDelayTask() {
    System.out.println(
      "Fixed delay task - " + System.currentTimeMillis() / 1000);
}

이 경우 마지막 실행 종료와 다음 실행 시작 사이의 기간이 고정됩니다. 작업은 항상 이전 작업이 완료 될 때까지 대기합니다.

이 옵션은 다시 실행하기 전에 이전 실행을 완료해야하는 경우에 사용해야합니다.

4. 고정 된 속도로 작업 예약

이제 고정 된 시간 간격으로 작업을 실행 해 보겠습니다.

@Scheduled(fixedRate = 1000)
public void scheduleFixedRateTask() {
    System.out.println(
      "Fixed rate task - " + System.currentTimeMillis() / 1000);
}

이 옵션은 각 작업 실행이 독립적 인 경우에 사용해야합니다.

예약 된 작업은 기본적으로 병렬로 실행되지 않습니다. 따라서 fixedRate 를 사용하더라도 이전 작업이 완료 될 때까지 다음 작업이 호출되지 않습니다.

예약 된 작업에서 병렬 동작을 지원하려면 @Async 어노테이션 을 추가해야합니다 .

@EnableAsync
public class ScheduledFixedRateExample {
    @Async
    @Scheduled(fixedRate = 1000)
    public void scheduleFixedRateTaskAsync() throws InterruptedException {
        System.out.println(
          "Fixed rate task async - " + System.currentTimeMillis() / 1000);
        Thread.sleep(2000);
    }

}

이제이 비동기 작업은 이전 작업이 완료되지 않은 경우에도 매초마다 호출됩니다.

5. 고정 금리 vs 고정 지연

Spring의 @Scheduled 어노테이션을 사용하여 예약 된 작업을 실행할 수 있지만 fixedDelay  및  fixedRate 속성을 기반으로  실행 특성이 변경됩니다.

fixedDelay의 속성 확인의 지연이 있음을하게 n 개의 작업의 실행 및 작업의 다음 실행 시작 시간 종료 시간 사이의 밀리 초.

이 속성은 작업의 한 인스턴스 만 항상 실행되도록해야 할 때 특히 유용합니다. 종속 직업의 경우 매우 유용합니다.

있도록, fixedRate의 속성은 모든에서 예약 된 작업 실행  n 개의 밀리 초. 작업의 이전 실행을 확인하지 않습니다.

이것은 작업의 모든 실행이 독립적 일 때 유용합니다. 메모리와 스레드 풀의 크기를 초과 할 것으로 예상하지 않는다면 fixedRate  가 매우 편리 할 것입니다.

수신 작업이 빨리 완료되지 않으면 "메모리 부족 예외"로 끝날 수 있습니다.

6. 초기 지연으로 작업 예약

다음으로 지연 (밀리 초)으로 작업을 예약 해 보겠습니다.

@Scheduled(fixedDelay = 1000, initialDelay = 1000)
public void scheduleFixedRateWithInitialDelayTask() {
 
    long now = System.currentTimeMillis() / 1000;
    System.out.println(
      "Fixed rate task with one second initial delay - " + now);
}

이 예제에서 fixedDelayinitialDelay 를 모두 사용하는 방법에 유의하십시오 . 작업은 initialDelay이후에 처음 실행되며 fixedDelay 에 따라 계속 실행됩니다 .

이 옵션은 작업에 완료해야하는 설정이있을 때 편리합니다.

7. Cron 표현식을 사용하여 작업 예약

때로는 지연과 속도가 충분하지 않으며 작업 일정을 제어하기 위해 cron 표현식의 유연성이 필요합니다.

@Scheduled(cron = "0 15 10 15 * ?")
public void scheduleTaskUsingCronExpression() {
 
    long now = System.currentTimeMillis() / 1000;
    System.out.println(
      "schedule tasks using cron jobs - " + now);
}

이 예에서는 매월 15 일 오전 10시 15 분에 실행되도록 작업을 예약하고 있습니다.

기본적으로 Spring은 cron 표현식에 서버의 현지 시간대를 사용합니다. 그러나 zone 속성을 사용하여이 시간대를 변경할 수 있습니다  .

@Scheduled(cron = "0 15 10 15 * ?", zone = "Europe/Paris")

이 구성을 사용하면 Spring은 매월 15 일 오전 10시 15 분에 파리 시간으로 실행되도록 어노테이션이 달린 메서드를 예약합니다.

8. 일정 매개 변수화

이러한 일정을 하드 코딩하는 것은 간단하지만 일반적으로 전체 앱을 다시 컴파일하고 다시 배포하지 않고도 일정을 제어 할 수 있어야합니다.

작업의 구성을 외부화하기 위해 Spring 표현식을 사용하고이를 속성 파일에 저장할 것입니다.

fixedDelay의 작업 :

@Scheduled(fixedDelayString = "${fixedDelay.in.milliseconds}")

있도록, fixedRate의 작업 :

@Scheduled(fixedRateString = "${fixedRate.in.milliseconds}")

크론 표현을 기반으로 작업 :

@Scheduled(cron = "${cron.expression}")

9. XML을 사용하여 예약 된 작업 구성

Spring은 예약 된 작업을 구성하는 XML 방식도 제공합니다. 이를 설정하기위한 XML 구성은 다음과 같습니다.

<!-- Configure the scheduler -->
<task:scheduler id="myScheduler" pool-size="10" />

<!-- Configure parameters -->
<task:scheduled-tasks scheduler="myScheduler">
    <task:scheduled ref="beanA" method="methodA" 
      fixed-delay="5000" initial-delay="1000" />
    <task:scheduled ref="beanB" method="methodB" 
      fixed-rate="5000" />
    <task:scheduled ref="beanC" method="methodC" 
      cron="*/5 * * * * MON-FRI" />
</task:scheduled-tasks>

10. 런타임에 동적으로 지연 또는 속도 설정

일반적으로 @Scheduled 어노테이션 의 모든 속성은 Spring 컨텍스트 시작시 한 번만 해결되고 초기화됩니다.

따라서 Spring에서 @Scheduled 어노테이션을 사용할 때 런타임에 fixedDelay 또는 fixedRate 값을 변경할 수 없습니다 .

그러나 해결 방법이 있습니다. Spring의 SchedulingConfigurer를 사용 하면 지연 또는 속도를 동적으로 설정할 수있는 더 많은 사용자 정의 가능한 방법을 제공합니다 .

Spring 구성 인 DynamicSchedulingConfig 를 만들고 SchedulingConfigurer 인터페이스를 구현해 보겠습니다 .

@Configuration
@EnableScheduling
public class DynamicSchedulingConfig implements SchedulingConfigurer {

    @Autowired
    private TickService tickService;

    @Bean
    public Executor taskExecutor() {
        return Executors.newSingleThreadScheduledExecutor();
    }

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        taskRegistrar.setScheduler(taskExecutor());
        taskRegistrar.addTriggerTask(
          new Runnable() {
              @Override
              public void run() {
                  tickService.tick();
              }
          },
          new Trigger() {
              @Override
              public Date nextExecutionTime(TriggerContext context) {
                  Optional<Date> lastCompletionTime =
                    Optional.ofNullable(context.lastCompletionTime());
                  Instant nextExecutionTime =
                    lastCompletionTime.orElseGet(Date::new).toInstant()
                      .plusMillis(tickService.getDelay());
                  return Date.from(nextExecutionTime);
              }
          }
        );
    }

}

알다시피 ScheduledTaskRegistrar # addTriggerTask 메서드 의 도움으로 Runnable 작업과 Trigger 구현을 추가하여 각 실행이 끝난 후 nextExecutionTime 을 다시 계산할 수 있습니다 .

또한 DynamicSchedulingConfig@EnableScheduling 으로 어노테이션을 달아 스케줄링이 작동하도록합니다.

결과적으로 TickService # tick 메서드가 각 지연 시간 후에 실행되도록 예약했으며 , 이는 getDelay 메서드에 의해 런타임에 동적으로 결정 됩니다.

11. 결론

이 기사에서는 @Scheduled 어노테이션구성하고 사용하는 방법에 대해 설명했습니다 .

스케줄링을 활성화하는 프로세스와 스케줄링 작업 패턴을 구성하는 다양한 방법에 대해 설명했습니다. 또한 지연 및 속도를 동적으로 구성하는 해결 방법도 보여주었습니다.

위에 표시된 예 는 GitHub 에서 찾을 수 있습니다 .