Spring Dynamic Scheduler REST API로 스케줄러 구현하기
@Scheduled
그동안은 어노테이션 사용해서 cron표현식으로 작성하는 스케줄러만 만들어봤다.
Task별로 시간이 다른 스케줄러를 구현할 일이 생겼는데, 그렇다고 1초에 한번씩 돌면서 체크할 일은 아닌거같고..
이참에 REST API로 스케줄러를 동적으로 생성하는 기능에 대해 찾아보게되었다.
1. ThreadPoolTaskScheduler를 설정하는 @Configuration 클래스를 생성해준다.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
@Configuration
@EnableScheduling
public class SchedulerConfiguration implements SchedulingConfigurer {
private final int POOL_SIZE = 10;
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setTaskScheduler(taskScheduler());
}
@Bean
public TaskScheduler taskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(POOL_SIZE);
scheduler.setThreadNamePrefix("ThreadPoolTaskScheduler");
scheduler.initialize();
scheduler.setRemoveOnCancelPolicy(true);
return scheduler;
}
}
2. Service단에서 생성자 주입 후 사용하면 된다.
서버 이중화로 Map이 각각 생겨서, 정확한 횟수나 조건은 로직 안에서 확인해준다. 삭제는 .cancel(false)로 진행한다.
import lombok.RequiredArgsConstructor;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ScheduledFuture;
import static java.util.Objects.nonNull;
@Component
@RequiredArgsConstructor
public class SchedulerService {
private final TaskScheduler taskScheduler;
private Map<String, ScheduledFuture<?>> schedulerMap = new HashMap<>();
public Map<String, ScheduledFuture<?>> schedulers(){
return schedulerMap;
}
public boolean exec(String testId){
try {
String schedulerId = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddhhmmss")) + testId; // schedulerId 생성
Date schedulerDate = Date.from(LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant());
ScheduledFuture<?> scheduledFuture = taskScheduler.scheduleAtFixedRate(
() -> {
try{
// 서버 이중화로 Task 중복 실행 방지 처리
// 로직 구현
} catch (Exception e){
LOGGER.error(StringUtil.extractStackTrace(e));
return;
}
}, schedulerDate, repeatTimeSec * 1000
);
// 스케줄러 map 저장
schedulerMap.put(schedulerId, scheduledFuture);
return true;
} catch (Exception e){
LOGGER.error(StringUtil.extractStackTrace(e));
return false;
}
}
public void stop(String schedulerId) {
try {
ScheduledFuture<?> scheduledFuture = schedulerMap.get(schedulerId);
if(nonNull(scheduledFuture)){
scheduledFuture.cancel(false);
schedulerMap.remove(schedulerId);
}
} catch (Exception e){
LOGGER.error(StringUtil.extractStackTrace(e));
}
}
}
얼마 전까지 의존성 주입할때 @Autowired를 쓰다가 private final로 바꿨다. 다들 final로 쓰시길래 차이가 궁금해서 찾아보았다.
@Autowired를 사용할 경우 순환 참조가 발생할 수 있고, final을 사용하면 불변성도 보장된다.
자세한 내용은 참고링크 마지막 참조!
참고
Dynamic Scheduling any REST API's
Spring boot provides an easy option of scheduling tasks which will be triggered when the interval or time is right. We can use the @Scheduled annotation to achieve this, but what if we want to schedule the jobs on certain events, or by invoking some servic
www.dynamicallyblunttech.com
ScheduledThreadPoolExecutor에서 주의할 점. (ThreadPool의 배신, 동작 방법을 알지 못하고 쓴 사람의 삽질)
ThreadPool을 조심해라? 이 글을 쓰게 된 이유는 나의 안일함 때문이었다. 주기적으로 장비의 데이터를 수집하고 메세지를 만들어서 상위 시스템으로 데이터를 전송해야 하는 업무를 하고 있어서 S
jeong-pro.tistory.com
How to setRemoveOnCancelPolicy for Executors.newScheduledThreadPool(5)
I have this: ScheduledExecutorService scheduledThreadPool = Executors .newScheduledThreadPool(5); Then I start a task like so: scheduledThreadPool.scheduleAtFixedRate(runnable, 0, seconds,
stackoverflow.com
생성자 주입을 @Autowired를 사용하는 필드 주입보다 권장하는 하는 이유
@Autowired를 사용하는 의존성 주입보다 생성자 주입(Constructor Injection)을 더 권장하는 이유는 무엇일까?
madplay.github.io