1. 개요
이 사용방법(예제)에서는 Spring @Scheduled 어노테이션 을 사용하여 작업을 구성하고 예약하는 방법을 설명합니다.
@Scheduled 로 메서드에 어노테이션을 달기 위해 따라야 할 간단한 규칙은 다음과 같습니다.
- 메서드는 일반적으로 void 반환 유형을 가져야 합니다(그렇지 않은 경우 반환된 값은 무시됨).
- 메서드는 매개변수를 기대하지 않아야 합니다.
2. 일정 지원 활성화
Spring에서 예약 작업 및 @Scheduled 어노테이션에 대한 지원을 활성화하려면 Java 활성화 스타일 어노테이션을 사용할 수 있습니다.
@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);
}
이 예제에서 어떻게 fixedDelay 와 initialDelay 를 모두 사용하고 있는지 확인하세요. 태스크는 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 Expressions를 사용하여 작업 구성을 외부화하고 속성 파일에 저장할 것입니다.
fixedDelay 작업 :
@Scheduled(fixedDelayString = "${fixedDelay.in.milliseconds}")
fixedRate 태스크 :
@Scheduled(fixedRateString = "${fixedRate.in.milliseconds}")
Cron 식 기반 작업 :
@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 으로 어노테이션을 달아 스케줄링이 작동하도록 합니다.
결과적 으로 getDelay 메서드 에 의해 런타임에 동적으로 결정되는 각 지연 시간 후에 실행하도록 TickService#tick 메서드를 예약했습니다 .
11. 작업을 병렬로 실행
기본적으로 Spring은 작업을 실행하기 위해 로컬 단일 스레드 스케줄러를 사용합니다 . 결과적으로 여러 @Scheduled 메서드가 있더라도 각 메서드는 스레드가 이전 작업 실행을 완료할 때까지 기다려야 합니다.
작업이 진정으로 독립적인 경우 병렬로 실행하는 것이 더 편리합니다. 이를 위해서는 필요에 더 잘 맞는 TaskScheduler 를 제공 해야 합니다.
@Bean
public TaskScheduler taskScheduler() {
ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
threadPoolTaskScheduler.setPoolSize(5);
threadPoolTaskScheduler.setThreadNamePrefix("ThreadPoolTaskScheduler");
return threadPoolTaskScheduler;
}
위의 예에서는 풀 크기가 5인 TaskScheduler 를 구성했지만 실제 구성은 특정 요구 사항에 맞게 미세 조정되어야 합니다.
11.1. 스프링 부트 사용
Spring Boot를 사용하면 훨씬 더 편리한 방법을 사용하여 스케줄러의 풀 크기를 늘릴 수 있습니다.
spring.task.scheduling.pool.size 속성 을 설정하는 것으로 충분합니다 .
spring.task.scheduling.pool.size=5
12. 결론
이 기사에서는 @Scheduled 어노테이션 을 구성하고 사용하는 방법에 대해 논의했습니다 .
예약을 활성화하는 프로세스와 예약 작업 패턴을 구성하는 다양한 방법을 다루었습니다. 또한 지연 및 속도를 동적으로 구성하는 해결 방법도 보여 주었습니다.
위에 표시된 예제 는 GitHub 에서 찾을 수 있습니다 .