Spring Batch - Job, Step
이번시간은 Job 에 대해서 자세히 알아보는 시간을 갖겠습니다.
Spring Batch 에서 ‘Job’ 이란 하나의 배치 작업에서 최상위 개념으로 특정 하나의 배치 작업이라고 생각하면 이해하기 쉽습니다.
Spring Batch 에서 Job 은 어떻게 실행할까?
우선 Spring Batch 에서 Job 은 어떻게 실행 하는지 알아보겠습니다.
이전에 우리가 생성한 ‘BasicJobConfiguration’ 클래스 기준으로 말씀드리겠습니다.
BasicJobConfiguration 클래스는 @Configuration 선언했습니다.
그럼 @bean 으로 선언한것은 Spring Ioc 컨테이너가 관리 하게 될 것 입니다.
프로젝트를 기동 합니다.
프로젝트 기동
그럼 우리가 선언한 메소드 basicJob 이 실행됩니다. 그리고 Job 이라는 객체를 반환 하게 됩니다.
어떻게 생성 하는지 한번 알아봅시다.
1 | public class JobBuilderFactory { |
get 메소드를 실행해서 우리가 Job 이름을 설정한 ‘BasicJob’ 와 메타데이터 조회 및 저장 전용 ‘jobRepository’ 객체 함께 ‘JobBuilder’ 객체를 생성합니다.
1 | public class JobBuilder extends JobBuilderHelper<JobBuilder> { |
1 | public class SimpleJobBuilder extends JobBuilderHelper<SimpleJobBuilder> { |
이번에는 start 메소드 입니다. 기존에 생성한 JobBuilder 클래스에 SimpleJobBuilder 클래스를 생성합니다. 동시에 우리가 Step 객체를 생성한 객체도 전달해서
steps 변수에 add 하게 됩니다.
1 | public class SimpleJobBuilder extends JobBuilderHelper<SimpleJobBuilder> { |
start 메소드와 크게 다르지 않습니다. steps 변수에 add 하게 됩니다.
1 | public class SimpleJobBuilder extends JobBuilderHelper<SimpleJobBuilder> { |
마지막인 build 메소드 입니다.
가장 중요한 부분은 SimpleJob 객체 생성 입니다. 그외 최상위 인터페이스 중 Job 이라는 객체가 있고 그중 하위 클래스 중 하나가 SimpleJob 입니다.
기존에 steps 변수에 저장했던 Step 객체를 SimpleJob 객체에 저장 하게 됩니다.
즉 Job 하나는 우리가 설정한 Step 을 저장하고 있다는 의미입니다.
배치 실행
기동시 우리가 설정한 Job, Step 은 Spring Batch 관리 하고 등록 되어 있다는것을 알아보았습니다.
그럼 등록했던 Job, Step 을 실행해야 하는데요.
이전에서 실행해봐서 알겠지만 기동과 동시에 Batch 를 실행 한다는 것을 알 수 있었습니다.
1 | public class BatchAutoConfiguration { |
우리가 등록한 Job, Step 을 실행하는 주범은 바로 JobLauncherApplicationRunner 입니다.
JobLauncherApplicationRunner 클래스는 Spring 에서 관리하는 ApplicationRunner 구현체 입니다.
즉 JobLauncherApplicationRunner 를 bean 으로 등록하고 최종적으로 SimpleJobLauncher 클래스의 run 메소드를 호출 하게 됩니다.
일단 다시 정리해서 말씀드리자면 Job 을 실행하기에 반드시 ‘Job’ 객체, ‘Job Parameter’ 이 두가지 입니다.
여기서 Job 은 실행 할수 있는 객체 단위라고 보면 될것 같습니다. 이후에 Job 객체에 대해 설명 하도록 하겠습니다. 그리고 Job parameter 는 해당 Job 을 실행할때 한번만 실행되지 않을겁니다.
오늘 Job 실행 했으면 내일도 실행 할것 입니다. 앞으로 실행될때 마다 각각의 실행을 분별 하기 용도라고 생각하시면 되겠습니다.
어느 하나의 Job ‘TestJob’ 이름이 있다면 파라미터는 ‘2021-06-06’ 지정 하고 내일은 ‘2021-06-07’ 로 지정 해서 앞으로 실행될 Job 을 구분짓는 것 입니다.
Job 실행시 ‘JobLauncher’ 가 담당 하게 됩니다.
그럼 JobLauncher 구현체인 SimpleJobLauncher run 메소드를 알아봅시다.
1 | public class SimpleJobLauncher implements JobLauncher, InitializingBean { |
여기서 JobExecution lastExecution = jobRepository.getLastJobExecution(job.getName(), jobParameters);
위에 설명한대로 job 이름, 실행되는 job parameter 를 인자값으로 가져오고 있습니다.
‘JobExecution’ 이라는 객체를 반환 받는데요. 앞서 설명한 배치 실행에 관리 및 히스토리를 보관하는 메타데이터 통해 JobExecution 객체를 가져옵니다.
즉 JobExecution 는 해당 Job 실행 단위로 보면 되겠습니다. 특정 Job 이라는 뼈대가 있다면 그것을 실행하는 주체가 JobExecution 입니다.
메타 데이터 통해 JobExecution 가져온 값이 만약이 있다면 여기서 job.isRestartable() false 면 이미 실행한 JobInstance 라고 경고가 나옵니다.
job.isRestartable() 는 옵션 설정인데 Job 재시작 여부 스위치 역활입니다. default 는 false 입니다.
job.getJobParametersValidator().validate(jobParameters);
만약 새로운 JobExecution 라면 입력한 파라미터 값 JobParametersValidator 객체 통해 올바른지 체크 하고
jobExecution = jobRepository.createJobExecution(job.getName(), jobParameters);
Job Name 하고 Job Parameter 인자값을 통해 새로운 JobExecution 을 생성 하게 됩니다.
SimpleJobLauncher.java
1 | @Override |
JobExecution 생성 하는 메소드 입니다.
JobInstance jobInstance = jobInstanceDao.getJobInstance(jobName, jobParameters);
jobName, jobParameters 통해 메타데이터 저장소(우리는 Mysql 로 설정했으니 해당 DB 테이블에서 가져온다.) 에서 JobInstance 를 가져오게 됩니다.
JobInstance 는 Job 의 실행 단위라고 보면 되겠습니다. 그럼 JobExecution 와 차이점이 무엇이냐
- JobInstance: 배치 처리에서 Job 이 실행될 때 하나의 Job 실행 단위
- JobExecution: 해당 JobInstance 대한 한 번의 실행을 나타내는 객체
즉 Job Name 을 ‘TestJob’, Job Parameter 를 ‘2021-05-05’ 통해 실행 한다면 한번의 JobInstance 만들어 지고 하나의 JobExecution 가 만들어 집니다.
그리고 Job Name 을 ‘TestJob’, Job Parameter 를 ‘2021-05-06’ 통해 실행 후 실패 한다고 가정시
한번의 JobInstance 만들어 지고, 하나의 JobExecution 가 만들어 집니다. 해당 JobInstance 가 실패한다면 다시 실행이 가능한데
여기서 실패한 Job Name 을 ‘TestJob’, Job Parameter 를 ‘2021-05-06’ 통해 다시 실행 한다면
JobInstance 는 이전에 만들어졌으니 생성은 안하지만 JobExecution 는 새롭게 생성 하게 됩니다.
즉 JobInstance (1) vs JobExecution(N) 관계 입니다.
이후 SimpleJobLauncher 클래스로 돌아와서
job.execute(jobExecution);
Runnable 통해 run 메소드를 호출 하게 되면 job.execute 가 실행되는데
1 | public abstract class AbstractJob implements Job, StepLocator, BeanNameAware, |
execute 메소드 에서는 doExecute 메소드를 호출 하게 됩니다.
1 | public class SimpleJob extends AbstractJob { |
기존에 Step 들을 steps 변수에 저장했었는데 for 문 통해 handleStep 메소드를 호출 하게 됩니다.
이후 Step 영역이기 때문에 다음 부분에서 설명을 하도록 하겠습니다.
Job 객체
Spring Batch 에서 사용되는 Job 객체에 대해 알아보겠습니다.
Job
Spring Batch ‘Job’ 은 최상위 인터페이지로 구성되어 있습니다.
대표적인 메소드를 보면
- getName(): 설정한 해당 job 의 이름을 가져오는 메소드 입니다.
- execute(): 설정한 해당 job 을 실행 하는 메소드 입니다.
- getJobParametersValidator(): 해당 job 을 실행 할려면 ‘job’, ‘parameter’ 인자값 통해 실행합니다. 이 중 두개의 인자값이 이전 실행한 job 과 중복이 일어나면 다시 실행 되지 않습니다.
예를들어 job: ‘testJob’, parameter: ‘2021-05-05’ 이렇게 파라미터 통해 실행한다면 다음에는 job: ‘testJob’, parameter: ‘2021-05-06’ 이렇게 다르게 인자값을 달리 해서 호출 해야 합니다.
AbstractJob
AbstractJob 입니다. 여기서 관리하는 대표적인 필드만 알아보겠습니다.
- name: 설정한 해당 job 의 이름 입니다.
- restartable: 해당 Job 이 재시작 여부 입니다.
- jobRepository: 앞서 설명한 배치 실행에 관련한 데이터 관리를 하는 메타데이터 저장소 입니다.
- jobParametersIncrementer: Job 실행하기 위해 이전에 실행한 파라미터와 비교해서 기록이 없다면 실행이 가능한데 이것을 가능 하도록 파라미터를 증가시킬수 있는 객체 입니다.
- jobParametersValidator: 위에 Job 객체를 설명하면서 Job 객체가 실행전 인자값으로 전달받은 해당 파라미터 값을 검증하는 객체 입니다.
- stepHandler: Job 안에 설정한 Step 을 실행하기 위한 핸드러 입니다.
SimpleJob
Spring Batch 여러 Job 객체 중 가장 대표적인 SimpleJob 입니다. 순차적으로 Step 을 실행 시키는 Job 입니다.
FlowJob
SimpleJob 은 순차적으로 실행 된다고 한다면 FlowJob 은 순차적으로 Step 을 실행하다가 어느 조건에 의해 Step2 로 실행 할지
아니면 Step3 로 진행 할지 분기 처리 방식 입니다.
JobInstance, JobExecution 자세히 알아보자
지금까지 Job 은 어떻게 실행하는지 알아보았습니다. 복잡한 로직 때문에 이해가 어려워 쉽게 이미지로 만들어보았습니다.
우리가 앞서 BasicJobConfiguration 클래스에서 Job 이름을 ‘basicJob’ 이라고 정했습니다.
그럼 Job 이름 basicJob 라고 구조체를 만들고 그 해당 Job 을 실행하면 실행단위을 나타내는 ‘JobInstance’ 객체가 생성 됩니다.
물론 여기서 먼저 JobParameter 객체를 만들고 JobName 와 JobParameter 객체를 가지고 JobInstance 생성 합니다.
그런 다음 생성한 JobInstance 의 하위 객체인 JobExecution 객체가 생성 됩니다. 실행단위을 나타내는 것이 ‘JobInstance’ 라면
JobExecution 는 해당 실행 단위를 더 구체적인 상태정보를 담고 있습니다.
즉 우리가 구성한 Job (JobName: basicJob) 이 실행하면 이전에 실행한 JobParameter 가 같지 않는 이상 JobInstance 가 생성되고
JobExecution 생성 됩니다. 하지만 여기서 비지니스 로직 수행 중 Exception 일어나고 다시 실행 하게 된다면
기존 JobInstance 는 유지 되고 JobExecution 한번 더 생성 하게 됩니다. 물론 성공 후에는 이전과 같은 JobParameter 라면 다시 Job 이 실행 되지 않습니다.
이러한 상태정보는 후에 자세히 배우게 되는데요 메타데이터 저장소에 모두 관리 하게 됩니다.
Spring Batch 에서 Step 은 어떻게 실행할까?
이번에는 Job 이러 Step 실행 과정에 대해 알아보겠습니다.
Step 은 다시 말씀드리자면 하나의 배치 작업 즉 Job 이 실행 하는 과정에서 그 안에 독립적으로 구성되고 역활 하는 하나하나가 Step 이라고 생각 하시면 됩니다.
프로젝트 기동
1 | @Bean |
우리가 만들었던 BasicJobConfiguration 클래스 확인 해보면 Step 을 설정하는 곳이 보입니다.
1 | public class StepBuilderFactory { |
StepBuilderFactory 가 StepBuilder 를 생성합니다. 이후 StepBuilder 는 하위 빌더 클래스 중 가장 기본이 되는 TaskletStepBuilder 생성 합니다.
1 | public class StepBuilder extends StepBuilderHelper<StepBuilder> { |
TaskletStepBuilder 클래스를 생성하고 우리가 배치 비지니스를 정의한 tasklet 인자로 받아서
1 | public class TaskletStepBuilder extends AbstractTaskletStepBuilder<TaskletStepBuilder> { |
Tasklet 전역변수에 값을 저장 합니다.
1 | public abstract class AbstractTaskletStepBuilder<B extends AbstractTaskletStepBuilder<B>> extends |
마지막으로 build 메소드를 호출 하게 된다면 AbstractTaskletStepBuilder 클래스에서 build 메소드로 호출 하게 됩니다.
그럼 TaskletStep step = new TaskletStep(getName());
getName 을 호출해서 우리가 ‘basicStep1’ 이라고 Step 이름을 지정한것을 불러오고 TaskletStep 클래스를 생성하게 됩니다.
step.setTasklet(createTasklet());
createTasklet() 메소드를 통해 바로 전에 TaskletStepBuilder 클래스에서 우리가 만든 비지니스 로직을 tasklet 변수에 저장한것을
불러오게 되고 생성한 TaskletStep 에 setter 하게 됩니다.
BasicJobConfiguration.java
1 | @Bean |
그 다음 이전에 설명한대로 최종적으로 Job 객체를 생성하기 위한 build() 메소드를 호출 하게 됩니다. 이 단계는 위 Job 단계에서 설명해드렸습니다.
배치 실행
1 | public class SimpleJob extends AbstractJob { |
상단에 SimpleJob 클래스가 doExecute 메소드까지 호출한다고 설명해드렸습니다.
여기서 Step 역활로 바뀌는데요
1 | public class SimpleStepHandler implements StepHandler, InitializingBean { |
Job 에서 생성한 JobInstance 를 가져와서
StepExecution lastStepExecution = jobRepository.getLastStepExecution(jobInstance, step.getName());
StepExecution 가져옵니다. 만약 메타데이터 저장소에 해당 Step 이 이전에 실패한 Step 이라면 가져 옵니다.
currentStepExecution = execution.createStepExecution(step.getName());
이전에 실행한 Step 이 아니라면 새로 생성을 합니다.
step.execute(currentStepExecution);
마지막으로 step 을 실행 하는 메소드로 진행 하게 됩니다.
1 | public abstract class AbstractStep implements Step, InitializingBean, BeanNameAware { |
stepExecution.setStartTime(new Date()); stepExecution.setStatus(BatchStatus.STARTED);
여기서 stepExecution 을 배치 실행 시간과 상태값을 변경 합니다. (나중에 메타데이터 저장소에 저장 하게 됩니다.)
doExecute(stepExecution);
doExecute 메소드를 호출하게 됩니다. 여기서 stepExecution 인자값으로 전달하는데 앞서 설명한 JobExecution Job 실행단위 관련된 상태 저장 관리 이고
stepExecution 도 배치 시작 날짜 및 상태값 등 실행 단위 관련된 데이터 관리 한다고 생각하시면 되겠습니다.
1 | public class TaskletStep extends AbstractStep { |
AbstractStep 구현체인 TaskletStep 클래스 doExecute 메소드로 호출하게 됩니다.
stepOperations.iterate(new StepContextRepeatCallback(stepExecution) {
여기서 iterate 통해 호출하게 되고 반복 수행하는데 여기서 TaskletStep 클래스인 doInTransaction 메소드를 실행 하게 되는 것으로 보입니다.
사실 어떻게 doInTransaction 메소드로 호출 되는 경로는 파악이 되지 않는 상태 입니다. 만약 아신다면 피드백 부탁드립니다.
1 | public class TaskletStep extends AbstractStep { |
result = tasklet.execute(contribution, chunkContext);
초기 우리가 BasicJobConfiguration 클래스에서 Step 에 tasklet 정의한 것을 execute 메소드 호출하는데요.
즉 이것은 Job 에 설정한 Step 에서 Tasklet 구현체에 호출 합니다.
우리가 Step 의 Tasklet 에 비지니스 로직을 구현한 곳에 가게 됩니다.
여기서 우리가 로직을 구성한것을 수행하게 됩니다.
Step 객체
Spring Batch 에서 사용되는 Step 인터페이스 대해 알아보겠습니다.
Step
Step
Spring Batch ‘Step’ 은 최상위 인터페이지로 구성되어 있습니다.
대표적인 메소드를 보면
- execute(): 설정한 Step 을 실행 하는 메소드 입니다.
AbstractStep
AbstractStep 입니다. 대표적인 필드만 알아보겠습니다.
- name: 설정한 해당 Step 의 이름 입니다.
- startLimit: Step 실행 제한 횟수 입니다.
- allowStartIfComplete: 해당 Step 이 실행 완료 후 재 실행 여부 입니다.
- jobRepository: Step 메타데이터 저장소 입니다.
TaskletStep
Step 구현체 중 가장 기본이 되는 객체가 TaskletStep 입니다.
TaskletStep 정리
Job 에서 TaskletStep 을 실행 한다면 BatchStatus 값은 ‘STARTED’, ExitStatus 값은 ‘EXECUTING’ 로 변경됩니다.
여기서 우리가 비지니스 로직을 구현한 Tasklet 을 수행하게 되는데요.
우리가 만든 비지니스 로직이 수행 하는데 중요한것은 loop 를 돌면서 수행한다는 점 입니다. 반드시 어느 시점에 loop 를 빠져나가고 싶으면
RepeatStatus.FINISHED
값을 return 해야 합니다. 참고로 null 로 return 해도 됩니다.
동시에 RepeatStatus 값은 ‘FINISHED’ 로 변경 됩니다.
최종 해당 Step 완료 되면 BatchStatus 값은 ‘COMPLETED’ 로 변경 됩니다.
Copyright 201- syh8088. 무단 전재 및 재배포 금지. 출처 표기 시 인용 가능.