[ Baeldung ] Scheduling in Spring with Quartz
Quartz vs Spring SchedulerShedlock — Spring Scheduler 중복 실행 제어하기동작원리Cron ExpressionJob 클래스에 스프링의 의존성 추가하기한 번만 실행시키는 TriggerJob 클래스에 파라미터 넘기기 → JobDataMapTroubleShooting
Quartz vs Spring Scheduler
Spring Scheduler
spring에서 built-in으로 제공하고, 프로젝트가 복잡하지 않고 단순히 scheduling과 bean 만 필요하다면 quartz 보다 spring scheduler가 나음
메인 메소드에
@EnableScheduling
을 달고, 원하는 메서드에 @Scheduler
를 달아서 사용메서드는 void의 return 이어야 함. 파라미터 가질 수 없음
quartz에서 제공해주는 기능
1. scheduler간의 clustering기능
2. shceduler 실패에 대한 후처리 기능
3. JVM 종료 이벤트를 캐치하여 스케줄러에게 종료를 알려주는기능
4. 여러가지 plugin
5. 이벤트 처리 (Job, Trigger)
즉 복잡한 설정이 필요할때는 quartz를 사용하면 된다.
Shedlock — Spring Scheduler 중복 실행 제어하기
Spring Scheduler
에서는 여러 대의 인스턴스에 대한 클러스터링 기능을 제공하지 않는다. 이 때문에 서버 인스턴스가 여러 대로 늘어나는 Scale Out
상황에서 중복으로 실행되어 문제를 일으킬 수 있다. 이를 위해 Lukas Krecan
님께서 만들어주신 Shedlock
이라는 오픈소스 솔루션을 적용하면 문제를 해결할 수 있다.동작원리
[ Baeldung ] Scheduling in Spring with Quartz
#Job, #JobDetail, #Trigger, #Scheduler
- 매번
Job
이 실행될 때마다JobDetail
instance를 만듦(Job 클래스의 instance를 만들지 않음).JobDetail
객체는Job
의 상세한 속성들을 갖고 있음
Cron Expression
Cron 표현식으로는 한 번만 실행시킬 수가 없음. 정기적인 작업에 대한 스케줄링
Job 클래스에 스프링의 의존성 추가하기
public final class AutowiringSpringBeanJobFactory extends SpringBeanJobFactory implements ApplicationContextAware { private transient AutowireCapableBeanFactory beanFactory; @Override public void setApplicationContext(final ApplicationContext context) { beanFactory = context.getAutowireCapableBeanFactory(); } @Override protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception { final Object job = super.createJobInstance(bundle); beanFactory.autowireBean(job); return job; } }
@Bean public SchedulerFactoryBean quartzScheduler() { SchedulerFactoryBean quartzScheduler = new SchedulerFactoryBean(); ... AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory(); jobFactory.setApplicationContext(applicationContext); quartzScheduler.setJobFactory(jobFactory); ... return quartzScheduler; }
@Component public class TownHallCreateJob implements Job { @Autowired private GameServerService gameServerService; @Override public void execute(JobExecutionContext context) throws JobExecutionException { System.out.println("Townhall Create Test"); } }
한 번만 실행시키는 Trigger
public void scheduleTownHallCreate(LocalDateTime startTime, Integer reservationId) { try { JobDetail jobDetail = JobBuilder.newJob(TownHallCreateJob.class) .withIdentity(reservationId.toString()).build(); Instant instant = startTime.atZone(ZoneId.systemDefault()).toInstant(); Trigger trigger = TriggerBuilder.newTrigger().withIdentity(reservationId.toString()) .startAt(Date.from(instant)) .build(); scheduler.scheduleJob(jobDetail, trigger); if (!scheduler.isStarted()) { scheduler.start(); } } catch (SchedulerException e) { throw new RuntimeException("예외"); } }
Job 클래스에 파라미터 넘기기 → JobDataMap
- JobDetail을 만들고 해당 JobDetail에서 JobDataMap 얻어서 값 넣기
JobDetail jobDetail = JobBuilder.newJob(TownHallCreateJob.class) .withIdentity(reservationId.toString()).build(); JobDataMap jobDataMap = jobDetail.getJobDataMap(); jobDataMap.put("test", 27);
- Job을 구현한 class에서 JobExecutionContext에서 JobDetail 얻고, JobDataMap 얻고 값 얻기
@Override public void execute(JobExecutionContext context) throws JobExecutionException { JobDataMap jobDataMap = context.getJobDetail().getJobDataMap(); System.out.printf("Townhall Create Test : %s\n", jobDataMap.get("test")); }
TroubleShooting
- Job 인터페이스를 구현한 Job 클래스는 Job이 실행될 때 마다 만들어지는 방식으로 동작함. 그래서 Job 사이에 데이터를 공유하기 위해서는 static 필드를 가져야 데이터 공유가 가능함
- While the job is the workhorse, Quartz doesn’t store an actual instance of the job class. Instead, we can define an instance of the Job using the JobDetail class.