Github Code : batch quartz demo
목표
1편에서 정리한 내용 바탕으로 기능을 구성해보자. 최종적으로 구현할 스케줄러는 2개며 Listener도 추가해보고 여러가지를 해보도록 하자
- 외부 서버에서 5분마다 새로운 상품 데이터 가져오는 배치 Job 실행
- 5분 간격으로 등록 상품들 이상 서버로 전달하는 배치 Job 실행
중요한 작업은 배치에서 처리 하기 때문에 사실 스케줄러는 크게 어려울게 없다.
Job 정의
- 외부 서버에서 5분마다 새로운 상품 데이터 가져오는 배치 Job 실행
@RequiredArgsConstructor
@Slf4j
public class FirstJob extends QuartzJobBean {
//batch JobLauncher
private final JobLauncher jobLauncher;
//batch JobRegistry
private final JobRegistry jobRegistry;
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
log.info("FIRST JOB START {}", LocalDateTime.now());
JobParameters params = new JobParametersBuilder()
.addLocalDateTime("FIRST_JOB_PARAM",LocalDateTime.now())
.toJobParameters();
try {
jobLauncher.run(jobRegistry.getJob("FIRST_BATCH"), params);
} catch (JobExecutionAlreadyRunningException | JobRestartException | JobInstanceAlreadyCompleteException |
JobParametersInvalidException | NoSuchJobException e) {
throw new RuntimeException(e);
}
}
}
- 5분 간격으로 등록 상품들 이상 서버로 전달하는 배치 Job 실행
@RequiredArgsConstructor
@Slf4j
public class SecondJob extends QuartzJobBean {
//batch JobLauncher
private final JobLauncher jobLauncher;
//batch JobRegistry
private final JobRegistry jobRegistry;
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
log.info("SECOND JOB START {}", LocalDateTime.now());
JobParameters params = new JobParametersBuilder()
.addLocalDateTime("SECOND_JOB_PARAM",LocalDateTime.now())
.toJobParameters();
try {
jobLauncher.run(jobRegistry.getJob("SECOND_BATCH"), params);
} catch (JobExecutionAlreadyRunningException | JobRestartException | JobInstanceAlreadyCompleteException |
JobParametersInvalidException | NoSuchJobException e) {
throw new RuntimeException(e);
}
}
}
Job Listener 설정
모든 Job들이 실행되기 전 동작하는 Listener
@Slf4j
public class GlobalJobListener implements JobListener {
@Override
public String getName() {
return GlabalJobListener.class.getName();
}
@Override
public void jobToBeExecuted(JobExecutionContext jobExecutionContext) {
log.info("GLOBAL JOB START BEFORE LISTENER : {}", jobExecutionContext.getJobDetail().getKey().getName());
JobDataMap jobDataMap = jobExecutionContext.getJobDetail().getJobDataMap();
}
@Override
public void jobExecutionVetoed(JobExecutionContext jobExecutionContext) {
log.info("JOB 실행 취소 : {}", jobExecutionContext.getJobDetail().getKey().getName());
}
@Override
public void jobWasExecuted(JobExecutionContext jobExecutionContext, JobExecutionException e) {
if (e != null) {
e.printStackTrace();
}
log.info("JOB 실행 완료 : {}", jobExecutionContext.getJobDetail().getKey().getName());
}
}
@Slf4j
public class FirstJobListener implements JobListener {
@Override
public String getName() {
return FirstJobListener.class.getName();
}
@Override
public void jobToBeExecuted(JobExecutionContext jobExecutionContext) {
log.info("FIRST START BEFORE LISTENER : {}", jobExecutionContext.getJobDetail().getKey().getName());
JobDataMap jobDataMap = jobExecutionContext.getJobDetail().getJobDataMap();
}
@Override
public void jobExecutionVetoed(JobExecutionContext jobExecutionContext) {
log.info("JOB 실행 취소 : {}", jobExecutionContext.getJobDetail().getKey().getName());
}
@Override
public void jobWasExecuted(JobExecutionContext jobExecutionContext, JobExecutionException e) {
if (e != null) {
e.printStackTrace();
}
log.info("JOB 실행 완료 : {}", jobExecutionContext.getJobDetail().getKey().getName());
}
}
Job이 실행되기 전/후에 실행되는 Listener다.
Scheduler에는 여러 개의 리스너를 추가할 수 있으며, Job별로도 서로 다른 리스너를 설정할 수 있습니다 . 어떻게 추가하는지는 아래 Config를 참고하시면 됩니다
Trigger 리스너도 추가할 수 있지만 여기서는 Job 리스너만 추가하겠습니다~
Scheduler Config
이제 일정 주기별로 실행시킬 Job을 정의 했다면 스케줄러에 해당 Job과 Triiger를 등록해주면 된다.
@Configuration
@RequiredArgsConstructor
public class TestQuartzConfig {
private final Scheduler scheduler;
@PostConstruct
public void schedulerConfig() throws SchedulerException {
JobKey jobKey = new JobKey("FIRST_JOB");
JobDetail firstJob = JobBuilder.newJob()
.withIdentity(jobKey)
.ofType(FirstJob.class)
.build();
Trigger firstTrigger = TriggerBuilder.newTrigger()
.withIdentity("FIRST_TRIGGER")
.startNow()
.withSchedule(CronScheduleBuilder.cronSchedule("0 0/5 * * * ?"))
.forJob(firstJob)
.build();
JobDetail secondJob = JobBuilder.newJob()
.withIdentity("SECOND_JOB")
.ofType(SecondJob.class)
.build();
Trigger secondTrigger = TriggerBuilder.newTrigger()
.withIdentity("SECOND_TRIGGER")
.startNow()
.withSchedule(CronScheduleBuilder.cronSchedule("0 0/5 * * * ?"))
.forJob(secondJob)
.build();
//Job 등록
scheduler.scheduleJob(firstJob, firstTrigger);
scheduler.scheduleJob(secondJob, secondTrigger);
//리스너 등록
scheduler.getListenerManager().addJobListener(new GlobalJobListener());
//위에서 정의한 JobKey로 특정 Job 리스너 설정
scheduler.getListenerManager().addJobListener(new FirstJobListener(), KeyMatcher.keyEquals(jobKey));
}
}
addJobListener 두개를 보면 특정 key 값을 지정하지 않으면 모든 Job이 실행되기 전에 Listener가 실행된다! 그래서 특정 Job Listener를 추가하고 싶다면 JobKey 객체로 지정해주면 된다
이외에도 addListener 메서드는 다양한 오버로딩이 제공되므로, 원하는 방식으로 설정할 수 있습니다.
결과
2024-11-21T21:25:00.003+09:00 INFO 21188 --- [eduler_Worker-1] o.t.s.schedule.GlobalJobListener : GLOBAL JOB START BEFORE LISTENER : FIRST_JOB
2024-11-21T21:25:00.004+09:00 INFO 21188 --- [eduler_Worker-1] o.t.s.schedule.FirstJobListener : FIRST START BEFORE LISTENER : FIRST_JOB
2024-11-21T21:25:00.007+09:00 INFO 21188 --- [eduler_Worker-2] o.t.s.schedule.GlobalJobListener : GLOBAL JOB START BEFORE LISTENER : SECOND_JOB
2024-11-21T21:25:00.009+09:00 INFO 21188 --- [eduler_Worker-2] o.t.springbatch5demo.schedule.SecondJob : SECOND JOB START : 2024-11-21T21:25:00.009248800
2024-11-21T21:25:00.009+09:00 INFO 21188 --- [eduler_Worker-1] o.t.springbatch5demo.schedule.FirstJob : FIRST JOB START 2024-11-21T21:25:00.009248800
...
2024-11-21T21:25:00.141+09:00 INFO 21188 --- [eduler_Worker-2] o.t.s.schedule.GlobalJobListener : JOB 실행 완료 : SECOND_JOB
2024-11-21T21:25:02.144+09:00 INFO 21188 --- [eduler_Worker-1] o.t.s.schedule.GlobalJobListener : JOB 실행 완료 : FIRST_JOB
2024-11-21T21:25:02.144+09:00 INFO 21188 --- [eduler_Worker-1] o.t.s.schedule.FirstJobListener : JOB 실행 완료 : FIRST_JOB
'개발 > spring boot' 카테고리의 다른 글
동시성(synchronized) (3) | 2025.01.04 |
---|---|
전략 패턴과 팩토리 메소드 패턴 리팩토링 (2) | 2025.01.03 |
[Spring] Quartz 도입기 1 (1) | 2024.10.31 |
[Spring] IoC, DI (0) | 2024.04.24 |
[Spring]BeanFactory와 ApplicationContext (0) | 2023.08.09 |