기본개념
- 배치 계층 구조에서 가장 상위에 있는 개념(하나의 배치작업 자체이다)
- 적어도 하나 이상의 step을 포함하고 있는 컨테이너
기본 구현체
- SimpleJob
- 순차적으로 Step을 실행시키는 Job
- 모든 Job에서 유용하게 사용할 수 있는 표준 기능을 갖고 있음
- FlowJob
- 특정한 조건과 흐름에 따라 Step을 구성하여 실행시키는 Job
- Flow 객체를 실행시켜서 작업을 진행함
Job 은 어떻게 만들어지고 어떻게 실행되는 걸까??
대부분 JobBuilderFactory
을 이용하여 Job 생성하게 된다.
jobBuilderFactory.get("simpleJob") // job 의 이름을 정한다.
.start(simpleStep1()) // 실행할 step 을 지정한다.
.next(simpleStep2()) // step 은 여러개 가질 수 있다.
.build(); // 최종적으로 Job 을 생성한다.
Job 구현체 내부 stepList에 step을 순차적으로 저장하여 최종적으로 저장하여 생성하게 된다.
실행은 (스프링 부트 기준)
JobLauncherApplicationRunner
안에 있는 JobLauncher
를 이용하여 Job을 실행한다.
JobLauncher
는 인터페이스이며 실 구현체는 SimpleJobLauncher
를 이용한다.
다음처럼 SimpleJobLauncher
를 이용하여 등록한 Job을 step 별로 실행하게 된다.
추가적으로 publisher
가 등록되어있다면 job 이 실행되고 이벤트도 발행시킬 수 있습니다.
그렇다면 애플리케이션에 여러 개의 Job 등록되어있으면 어떻게 될까?
별 다른 설정을 하지 않고 애플리케이션을 실행하면 등록된 모든 Job을 실행하게 된다.
특정 job 만 실행시키고 싶다면 application.yml
에 spring.batch.job.names: ${job.name:NONE}
옵션을 추가하여 program arguments에 job 이름을 넣어주어 실행시켜주면 된다.
그렇다면 이 넘겨준 Job 이름은 어디서 사용되고 어떻게 한 가지 Job 만 실행되는 걸까?
Spring boot 가 실행될때
JobLauncherApplicationRunner
을 자동적으로 생성하여 등록하는 부분을 찾아보면 됩니다!
등록되는 Config 이름은 BatchAutoConfiguration
입니다.
코드를 보면 properties 또는 args로 넘겨준 jobName 이 있다면 생성할 때 runner에 등록을 합니다.
없다면 별다른 설정없이 생성됩니다.
이렇게 생성되었다면 JobLauncherApplicationRunner
가 실질적으로 SimpleJobLauncher
을 실행할 때 모든 Job을 실행할지 특정 Job 만 실행할지 결정하게 됩니다.
해당 메서드에서 executeLocalJobs
, executeRegisteredJobs
두 개의 메서드가 불립니다.
여기서 executeLocalJobs
은 JobName 이 있다면 해당 job을 찾아 실행하게 됩니다.
그 후 executeRegisteredJobs
가 불리지만 JobLauncherApplicationRunner
에 jobName 이 설정되어 있다면 해당 메서드는 실행되지 않고 종료됩니다.
앞선 살펴본 코드를 요약하자면 다음과 같습니다.
- jobName을 넘겨준다고 해서 해당하는 Job의 Bean 만 등록이 되는 것이 아니다.
- 단지 jobName으로 여러 개의 job 들 중 이름이 같은 job 만 실행이 된다.
JobInstance, JobExecution에 대해 알아보자
JobExecution
, JobInstance
는 위 그림과 같은 구조로 생성이 됩니다.
그림만으로도 충분히 이해하실 수 있지만 코드와 설명을 통해 조금 더 알아보죠!
JobInstance
- Job 이 실행될 때마다 BATCH_JOB_INSTANCE에 저장할 Entity
- 이미 동일한 JobParameter, JobName 이 존재하면 저장되지 않음
- 중요) 저장이 안 된다는 거지 Job 실행이 안 되는 건 아닙니다.
- 즉 동일한 Job에 대해서 JobParameter 가 다르다면 계속해서 생성됩니다.
- 관계
- Job (1) : JobInstance(N)
- JobInstance (1) : JobParameter(1)
JobExecution
- JobInstance 가 정상적으로 성공했는지? 아니면 실패했는지 기록합니다.
- 성공시간, 시작시간 , Job 실행 상태 기록
- 기록하는 테이블은 BATCH_JOB_EXECUTION입니다.
- 관계
- JobInstance (1) : JobExecution(N)
앞서 언급한 것 처럼 중요한 부분은 기존에 동일한 JobInstance 있다고 해서 실행하고자 하는
Job 이 무조건 실패하는 건 아닙니다.
(제가 처음 batch 을 접했을 땐 그런 줄 알았습니다. 😭)
이미 존재하는 JobInstance에 대해 동일한 Job, JobParameter을 실행시 다음과 같이 동작합니다.
(설명하는 내용은 JobInstance 존재하지 않아도 동일하게 검증을 진행합니다.)
첫 번째 검증
jobInstanceDao
로 부터JobInstance
를 가져옵니다.JobInstance
이용jobExecutionDao
으로부터 가장 마지막으로 등록된JobExecution
가져옵니다.- 가져온
JobExecution
의 하위의StepExecution
을 확인하여 실행을 할지 안 할지 결정합니다.StepExecution
은 step을 설명할 때 다시 설명하도록 하겠습니다.
코드는 SimpleJobLauncher
의 run() 메서드를 따라가면서 보시면 됩니다.
해당 코드가 앞선 첫 번째 단계에 대해 설명한 부분입니다.
위 과정을 통과한다면 다시 다음과 같은 동작을 합니다.
두 번째 검증
jobInstanceDao
로 부터JobInstance
를 가져옵니다.JobInstance
이용jobExecutionDao
으로부터 등록된 모든JobExecution
가져옵니다.- 모든
JobExecution
순회하며 상태를 확인하여 확인하여 실행을 할지 안 할지 결정합니다. - 위 과정이 이상이 없다면 새로운
jobExecution
을 만들어 저장합니다.
해당 코드는 SimpleJobLauncher
run() 메서드 안의 jobRepository.createJobExecution
을 참고
하여 보시면 됩니다.
jobRepository의 구현체는 SimpleJobRepository
입니다.
해당 과정을 요약하자면
이미 등록된 JobInstance
가 존재하여도 jobExecution
의 상태에 따라 Job 실행 여부가 결정됩니다.
여기까지 Job 에 관련된 동작원리 및 속성에 대해 알아보았는데요!
모든 동작원리, 속성에 대해 설명하지 않았지만 제가 생각하는 중요한 부분은 짚고 넘어간거 같습니다.
혹시라도 글 본문에 잘못된 내용이 있거나 이 외에도 중요한내용이 있다면
댓글로 남겨주시면 반영하도록 하겠습니다.
감사합니다. 🙇♂️