스케줄링은 어떤 작업(job)에 대해서 지정된 기간 혹은 시간에 수행하도록 하는 것이다. 스케줄 작업을 처리하기 위해서는 Quartz Scheduler 와 같은 외부 라이브러리를 이용하거나 스프링부트에서 @Scheduled 어노테이션을 이용해서 간단하게 스케줄(배치) 작업을 할 수 있다.
이 글에서는 스프링 부트에서 @Scheduled 어노테이션을 이용하여 스케줄링을 사용해 보고, 공부한 기록을 남긴다.
스프링 부트에서 @Scheduled 를 이용한 스케줄링의 사용은 간단하다.
아래와 같이 Application 클래스에서 @EnableScheduling 어노테이션을 넣어서, 스케줄링 기능을 사용할 수 있는 상태로 만들어 준다.
@EnableScheduling // 스케줄링 기능을 enable 함
@SpringBootApplication
public class AnythingTestApplication {
...
}
다음으로 아래와 같이 스케줄링 기능을 이용해서 수행할 Job에 @Scheduled 어노테이션으로 스케줄 시간을 설정해 준다.
@Service
@Slf4j
public class SchedulerService {
@Scheduled(initialDelay = 10000, fixedDelay = 10000)
public void runAfterTenSecondsRepeatTenSeconds() {
log.info("10초 후 실행 => time : " + LocalTime.now());
}
}
위 예제는 프로그램 시작 후, 10초 후에 처음으로 job을 수행하고, 매 10초 마다 반복 수행된다. 결과는 아래와 같이 10초마다 로그가 찍히는 것을 볼 수 있다.
@Scheduled 어노테이션에 설정할 수 있는 옵션은 아래와 같다.
@Scheduled(cron = "5 * * * * *")
public void something() {
}
@Scheduled(cron = "5 * * * * *", zone="Asia/Seoul")
public void something() {
}
@Scheduled(fiexedDelay=1000L) // 이전 something() 수행 완료 후 1초 뒤에 다시 시작
public void something() {
}
@Scheduled(fixedRate=1000L) // 매 1초마다 something() 수행
public void something() {
}
fixedDelay와 차이가 있으므로 잘 구분해서 사용하도록 하자.
@Scheduled(initialDelay=1000) // App 실행 완료 후 1초 후에 something 잡이 수행됨.
public void something() {
}
모든 @Scheduled 작업은 Spring에 의해 생성 된 한 개의 thread pool에서 실행된다. 그래서 하나의 Schedule이 돌고 있다면 그것이 다 끝나야 다음 Schedule이 실행되는 단점이 있다.
아래와 같이 예제를 만들어서 1초마다 실행되는 로그를 살펴보면 스케줄이 실행되는 thread를 확인할 수 있다.
@Scheduled(fixedRate = 1000)
public void runEveryTenSecondsOne() {
log.info("runEveryTenSecondsOne time : " + LocalTime.now());
log.info("runEveryTenSecondsOne thread: " + Thread.currentThread().getName());
}
@Scheduled(fixedRate = 1000)
public void runEveryTenSecondsTwo() {
log.info("runEveryTenSecondsTwo time : " + LocalTime.now());
log.info("runEveryTenSecondsTwo thread: " + Thread.currentThread().getName());
}
실제로 로그를 보면 같은 쓰레드로 확인 될 것이다.
이러한 단점은 스프링 부트에서 설정을 통해 schedule에 대한 thread pool을 생성하고 그 thread pool을 사용하여 모든 스케줄 된 작업을 실행하여 보완할 수 있다.
아래는 설정 방법에 대한 예제이다.
@Configuration
public class SchedulerConfig implements SchedulingConfigurer {
private final int POOL_SIZE = 10;
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
final ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
threadPoolTaskScheduler.setPoolSize(POOL_SIZE);
threadPoolTaskScheduler.setThreadNamePrefix("test-scheduled-task-pool-");
threadPoolTaskScheduler.initialize();
taskRegistrar.setTaskScheduler(threadPoolTaskScheduler);
}
}
스케줄이 돌고있는 메소드에서 현재 스레드의 이름을 로깅하면 아래와 같은 출력이 표시된다.
생성된 thread pool을 이용하여 여러 개의 스케줄을 동시에 처리할 수 있다.