몽수의 Spring Scheduler
스프링 스케줄러(Spring Scheduler)
스프렝 스케줄러란?
- Spring Framework에서 제공하는 기능
- 특정 작업을 주기적으로 실행할 수 있도록 지원한다.
- 이를 통해 배치 작업이나 정기적인 백그라운드 작업을 간편하게 설정할 수 있다.
- @Schedlued 어노테이션을 사용하여 설정한다.
스프링 스케줄러의 기본 개념
1. 스케줄링이란?
- 스케줄링은 특정 시간 간격이나 특정 시간에 작업을 실행하는 것을 의미한다.
- 예를 들어, 매일 자정에 데이터 백업 작업을 실행하거나 매 시간마다 로그 파일을 정리하는 작업을 설정할 수 있다.
2. 스프링 스케줄러의 주요 기능
- 주기적으로 반복되는 작업을 설정할 수 있다.
- 고정된 간격으로 작업을 실행하거나, 특정 시간에 작업을 실행하는 등 다양한 스케줄링 옵션을 제공한다.
- 별도의 스레드에서 작업을 실행하며 메인 애플리케이션의 흐름에 영향을 주지 않는다.
스프링 스케줄러 설정 방법
1. 스케줄링 활성화
스프링 스케줄러를 사용하려면 설정 클래스에 @EnableScheduling 어노테이션을 추가하여 스케줄러 기능을 활성화해야한다.
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
@Configuration
@EnableScheduling
public class SchedulerConfig {
// 추가 설정 필요 시 여기에 작성
}
2. 스케줄링 메서드 작성
- 스케줄링 할 작업을 담는 메서드 @Scheduled 어노테이션을 추가한다.
- @Scheduled 어노테이션의 속성을 사용하여 작업 실행 간격이나 실행 시간을 설정할 수 있다.
@Scheduled 어노테이션
이 어노테이션을 통해 다양한 속성을 사용하여 작업 스케줄을 설정한다. 주요 속성은 다음과 같다.
1. fixedRate
- 고정된 시간 간격(밀리초 단위)으로 작업을 실행한다.
- 이전 작업이 완료되었는지 여부와 상관없이 설정된 간격으로 작업을 실행한다.
@Scheduled(fixedRate = 5000)
public void fixedRateTask() {
System.out.println("Fixed rate task - " + System.currentTimeMillis() / 1000);
}
-> 이 메서드는 매 5초마다 실행됨
2. fixedDelay
- 이전 작업이 완료된 후 고정된 시간 간격(밀리초 단위)으로 작업을 실행한다.
- 이전 작업이 완료된 시점부터 설정된 간격으로 작업을 실행한다.
@Scheduled(fixedDelay = 5000)
public void fixedDelayTask() {
System.out.println("Fixed delay task - " + System.currentTimeMillis() / 1000);
}
-> 이 메서드는 이전 실행이 완료된 후 5초의 지연시간을 두고 메서드가 다시 실행됨
3. cron
- 크론 표현식을 사용하여 작업을 스케줄링한다.
- 크론 표현식은 초, 분, 시간, 일, 월, 요일을 조합하여 특정 시간에 작업을 실행할 수 있다.
@Scheduled(cron = "10 0 * * * *")
public void cronTask() {
System.out.println("Cron task - " + System.currentTimeMillis() / 1000);
}
cron = "초, 분, 시간, 일, 월, 요일"
-> 이 메서드는 매 10초마다 실행됨
종합 예제
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Configuration
@EnableScheduling // 스케줄링 기능 활성화
public class SchedulerConfig {
}
@Component
public class ScheduledTasks {
// 매 5초마다 실행
@Scheduled(fixedRate = 5000)
public void fixedRateTask() {
System.out.println("Fixed rate task - " + System.currentTimeMillis() / 1000);
}
// 이전 작업이 완료된 후 5초 후에 실행
@Scheduled(fixedDelay = 5000)
public void fixedDelayTask() {
System.out.println("Fixed delay task - " + System.currentTimeMillis() / 1000);
}
// 매 분 0초에 실행
@Scheduled(cron = "0 * * * * *")
public void cronTask() {
System.out.println("Cron task - " + System.currentTimeMillis() / 1000);
}
}
스프링 스케줄러의 주의 사항
- 스케줄링된 메서드는 void 반환 타입을 가져야 하며, 매개변수를 가지지 않아야 한다.
- 스케줄링된 메서드 내부에서 예외가 발생하면 스케줄러는 해당 작업을 다시 시도하지 않는다.
- 스케줄링 작업이 오래 걸리는 경우 별도의 스레드풀을 구성하거나 비동기 처리를 고려해야한다.
스프링 스케줄러의 동작 과정
1. ScheduledAnnotationBeanPostProcessor 클래스가 Bean으로 등록됨
- 스프링 애플리케이션이 시작되면, 스프링 컨텍스트는 ScheduledAnnotationBeanPostProcess 클래스를 빈으로 등록한다.
- 해당 어노테이션이 붙은 클래스는 @Scheduled 어노테이션이 붙은 메서드를 스켄하고 관리하는 역할을 한다.
@Configuration
@EnableScheduling
public class SchedulingConfig {
// 스케줄링 설정 클래스
}
2. postProcessAfterInitialization() 메서드 실행
- ScheduledAnnotationBeanPostProcessor는 스프링 빈이 초기화 된 후 postPorcessAfterInitialization() 메서드 실행
- 이 메서드는 모든 빈을 스캔하여 @Scheduled 어노테이션이 붙은 메서드를 찾고, 이를 내부 맵에 저장한다.
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (!this.processScheduled(bean)) {
return bean;
}
return proxyScheduledMethods(bean);
}
3. processScheduled() 메서드 호출
- postProcessAfterInitialization() 메서드는 processScheduled() 메서드를 호출하여 @Scheduled 어노테이션이 붙은 메서드를 처리한다.
- 이 메서드는 스케줄링된 메서드를 찾아서 적절한 스케줄링 정보를 설정한다.
private boolean processScheduled(Object bean) {
// @Scheduled 어노테이션이 붙은 메서드를 처리
}
4. processScheduled() 메서드에서 작업 스케줄링
- processScheduled() 메서드는 @Scheduled 어노테이션의 속성(cron, fixedReate, fixedDelay)에 따라 작업 스케줄링
- 여기서는 cron 속성을 사용하는 경우를 예로 설명한다.
private void processScheduled(Object bean) {
// @Scheduled 어노테이션의 속성에 따라 작업을 스케줄링
if (cron != null) {
this.scheduledTaskRegistrar.scheduleCronTask(new CronTask(runnable, cron));
}
}
5. ScheduledTaskRegistrar.scheduleCronTask() 호출
- processScheduled() 메서드는 ScheduledTaskRegistrar의 scheduleCronTask() 메서드를 호출하여 작업을 스케줄링
- 이 메서드는 작업과 크론 표현식을 받아서 스케줄링 작업을 등록
public void scheduleCronTask(CronTask task) {
this.scheduledCronTasks.add(task);
this.scheduleCronTask(task.getRunnable(), task.getExpression());
}
6. ScheduledExecutorService에 작업 등록
- ScheduledTaskRegistrar는 ScheduledExecutorService의 구현체에 작업과 딜레이 정보를 넘겨 작업을 등록
- ScheduledExecutorService는 작업을 실행할 스레드 풀을 관리한다.
ScheduledExecutorService executor = Executors.newScheduledThreadPool(poolSize);
executor.schedule(() -> {
// 작업 실행
}, delay, TimeUnit.MILLISECONDS);
7. Delay가 지나면 작업 실행
- ScheduleExecutorService는 설정된 딜레이가 지나면 스레드 풀에서 스레드를 할당하여 작업의 run() 메서드를 실행한다.
- 이 메서드는 스케줄링된 작업을 실제로 실행한다.
executor.schedule(() -> {
// 작업 실행
}, delay, TimeUnit.MILLISECONDS);
8. 작업 재등록
- 작업이 실행된 후 run() 메서드는 ScheduleExecutorService에 작업을 다시 등록하여 주기적으로 실행한다.
- 이로 인해 작업은 설정된 딜레이 후에 다시 실행
executor.schedule(() -> {
// 작업 실행 후 다시 등록
}, delay, TimeUnit.MILLISECONDS);
9. 무한 반복
요약된 동작 과정
1. 스프링 컨텍스트 초기화: 스프링 부트 애플리케이션이 시작될 때 스프링 컨텍스트가 초기화
2. @EnableScheduling 확인 : 스프링 컨텍스트는 @EnableScheduling 어노테이션이 붙은 클래스를 찾아 스케줄링 기능을 활성화
3. ScheduledAnnotationBeanPostProcessor 등록 : 스프링 컨텍스트는 해당 클래스는 빈으로 등록한다.
4. 3번에서 빈으로 등록한 클래스를 생성 후 postProcessAfterInitialization() 메서드 실행
5. @Scheduled 메서드 스캔 : postProcessAfterInitialization()가 실행되어 스프링 컨텍스트에서 모든 빈을 스캔하여 @Scheduled 어노테이션이 붙은 메서드를 찾아내고, 이를 내부 맵에 저장한다.
6. processScheduled() 호출: postProcessAfterInitialization()에서 호출된 메서드로 스케줄링 정보를 설정해준다.
7. cron, fixedDelay등 옵션에 따라 알맞는 Task 진행한다.
8. ScheduledTaskRegistrar : 스케줄링 작업을 등록한다.
9. ScheduledExecutorService : ScheduledTaskRegistrar에 등록된 작업을 주기적으로 실행 시켜준다.
- 딜레이가 지나면 작업 실행
- 작업 실행 후 다시 등록
- 무한 반복