이 게시글은 인프런 정수원님의 강의를 복습하며 작성한 글입니다
JobExplorer, JobRegistry, JobOperation
Spring Batch는 Batch 처리 도움을 주기 위해 위의 객체를 제공한다. 위의 객체에 대한 정리는 아래에서 살펴보자.
JobExplorer
- JobExplorer는 JobRepository의 읽기전용 모드임.
- 실행 중인 Job의 실행 정보인 JobExecution, StepExecution을 조회할 수 있음. → 꺼내서 Stop하는데 도움됨.
JobRegistry
- 생성된 Job을 자동으로 등록, 추적 및 관리하며 여러 곳에서 Job을 생성한 경우 스프링 컨테이너에서 Job을 수집해서 사용할 수 있음.
- Job Registry의 기본 구현체는 MapJobRegistry임. Map기반이고 Key는 JobName임.
- Job 등록은 스프링 컨테이너가 시작할 때, JobRegistryBeanPostProcessor에 의해서 자동으로 JobRegistry에 Job을 등록시켜줌. 따라서 JobRegistryBeanPostProcessor를 스프링 빈으로 등록해야함.
- Job Registry는 내부적으로 Job Factory를 가지고 있고, Job이 필요할 때 꺼내와서 Job을 생성함.
JobOperator → 주로 이걸 사용함.
- JobExplorer, JobRepository, JobRegistry, JobLauncher를 가지고 있음.
- Batch Job의 중단, 재시작, Job 요약 등의 모니터링이 가능함.
- 기본 구현체로 SimpleJobOperator가 제공됨.
JobExplorer, JobRegistry, JobOperation의 활용방안
위 객체들로 개발자는 어떤 일을 할 수 있을까? JobOperation을 이용해서 Job을 쉽게 실행할 수 있고, 실행중인 Job을 멈추는 작업을 할 수 있기 때문에 활용 방안이 무궁무진하다. 만약에 나라면 토이 프로젝트에서 Session Listener를 이용해서 필요한 작업을 주기적으로 처리할 수도 있고, Controller를 하나 따서 요청이 올 때 마다 Job을 실행하는 쪽에도 활용할 것이다.
JobOperation은 JobRegistry를 가짐.
JobRegistry는 기본적으로 스프링이 등록해주는 객체다. JobRegistryPostBeanProcessor를 등록해주면, 이 빈 후처리기는 JobRegistry에 빈으로 등록된 모든 Job을 JobFactory로 등록해준다. 그리고 JobRegistry는 getJob()을 통해서 JobRegistry가 가지고 있는 JobFactory를 꺼내와 Job을 가질 수 있다.
JobOperation은 내부적으로 JobRegistry를 참조한다. 따라서 JobRegistry를 통해서 Job을 가지고 와서 그 Job을 실행할 수 있다. 이 때, JobName 정도만 전달해주면 Job을 찾을 수 있고, 찾아온 Job에 Job Parameter를 더하면서 실행할 수 있게 된다.
JobExplorer, JobRegistry, JobOperation 코드 작성
이번 코드는 Job을 하나 구성한다. 그리고 JobRegistryBeanPostProcessor를 등록해서 JobRegistry에 Job을 저장할 수 있게 한다. 그리고 컨트롤러를 하나 따서, 컨트롤러에 요청이 오면 JobOperation을 통해서 Job을 실행할 수 있도록 한다.
JobRegistryBeanPostProcessor 등록
@Bean
public JobRegistryBeanPostProcessor jobRegistryBeanPostProcessor(JobRegistry jobRegistry) {
JobRegistryBeanPostProcessor jobRegistryBeanPostProcessor = new JobRegistryBeanPostProcessor();
jobRegistryBeanPostProcessor.setJobRegistry(jobRegistry);
return jobRegistryBeanPostProcessor;
}
JobRegistry는 스프링 빈이 시작할 때 등록해준다. 개발자는 DI 받은 JobRegistry를 JobPostBeanProcessor에 등록해준다. 이 과정이 거쳐지면 JobRegistry는 빈으로 등록되는 Job을 가지게 된다.
Job 구성
@RequiredArgsConstructor
@Configuration
public class JobOperatorRetryConfig {
private final JobBuilderFactory jobBuilderFactory;
private final StepBuilderFactory stepBuilderFactory;
private final JobRegistry jobRegistry;
@Bean
public Job jobOperationRetryJob() {
return jobBuilderFactory.get("jobOperationRetryJob")
.incrementer(new RunIdIncrementer())
.start(step1())
.next(step2())
.build();
}
@Bean
public Step step1() {
return stepBuilderFactory.get("jobOperationRetryStep")
.tasklet(new Tasklet() {
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
System.out.println("step1 executed!");
Thread.sleep(10000);
System.out.println("step1 complted!");
return RepeatStatus.FINISHED;
}
})
.build();
}
@Bean
public Step step2() {
return stepBuilderFactory.get("jobOperationRetryStep2")
.tasklet(new Tasklet() {
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
System.out.println("step2 executed!");
Thread.sleep(10000);
System.out.println("step2 complted!");
return RepeatStatus.FINISHED;
}
})
.build();
}
다음과 같이 Batch Job을 구성했다. 기본적으로는 각 Step마다 10초씩 쉬어가도록 했는데, 이건 Stop 하는 동작도 구현하도록 한 것이다.
Controller 구성
@Controller
@RequiredArgsConstructor
public class JobController {
private final JobOperator jobOperator;
private final JobExplorer jobExplorer;
@RequestMapping("/batch/new")
public String jobStart(@RequestParam(name = "id") String id) throws JobInstanceAlreadyExistsException, NoSuchJobException, JobParametersInvalidException {
Set<String> jobNames = jobOperator.getJobNames();
for (String jobName : jobNames) {
jobOperator.start(jobName, id + "abc");
}
System.out.println("Job Start!");
return "batch Job start!";
}
@RequestMapping("/batch/stop")
public String jobStop() throws JobInstanceAlreadyExistsException, NoSuchJobException, JobParametersInvalidException, NoSuchJobExecutionException, JobExecutionNotRunningException {
System.out.println("Job Stop!");
Set<String> jobNames = jobOperator.getJobNames();
Set<Long> jobOperationRetryJob = jobOperator.getRunningExecutions("jobOperationRetryJob");
for (Long aLong : jobOperationRetryJob) {
jobOperator.stop(aLong);
}
return "batch Job stop!";
}
@RequestMapping("/batch/re")
public String jobRetry(@RequestParam(name = "id") String id) throws JobInstanceAlreadyExistsException, NoSuchJobException, JobParametersInvalidException, JobInstanceAlreadyCompleteException, NoSuchJobExecutionException, JobRestartException {
System.out.println("Job Retry!");
Set<String> jobNames = jobOperator.getJobNames();
JobInstance lastJobInstance = jobExplorer.getLastJobInstance("jobOperationRetryJob");
JobExecution lastJobExecution = jobExplorer.getLastJobExecution(lastJobInstance);
jobOperator.restart(lastJobExecution.getId());
return "batch Job Retry!";
}
}
- Controller를 통해 Job의 실행, 실행중인 Job의 중단, 중단된 Job의 재시작을 할 수 있도록 했다.
- JobOperation을 통해서 start / stop / restart를 통해서 구현할 수 있다.
- 이 때, JobOperation에 jobExecution이나 jobExecutionId를 던져줘야하는데 이것들은 JobExplorer, JobOperator에서 충분히 찾을 수 있다.
- 기본적으로 JobOperation에 jobNames()를 하면 Collection 자료 구조로 반환이 되는데, 이것은 JobRegistry 내부에 여러 Job이 등록될 수 있기 때문이다.
- retry 메서드는 lastJobInstance()를 가져와야한다. 왜냐하면 실패한 잡은 가장 마지막에 있기 때문이다.
JobOperation을 통한 JobStop 시 유의사항.
jobOperation을 통해 Job을 중지 시킬 수 있다. 이 때 유의할 사항은 Stop이 실행되더라도, 현재 실행 중인 Step까지는 모두 완료하고 Job을 중단한다는 것이다. 예를 들어 Step1, Step2를 해야하는데 Step1을 실행하는 중 Stop 명령을 받으면 Step1을 완료한 상태에서 Job을 종료한다.
테스트 코드 확인
'Spring > Spring Batch' 카테고리의 다른 글
Spring Batch : Step Skip (0) | 2022.03.19 |
---|---|
Spring Batch : Partition Step (0) | 2022.03.19 |
Spring Batch : Listener (0) | 2022.03.17 |
SpringBatch : SpringBatch Test 하기 (0) | 2022.03.16 |
SpringBatch : CompositeItemProcessor / ClassifierCompositeItemProcessor (0) | 2022.03.13 |