내용 목차
본 장에서는 JBatch의 개념과 구성 요소에 대해서 설명한다.
배치 처리방식은 널리 사용되는 워크로드 패턴으로, 벌크 지향적이고 상호호환을 하지 않으며 백그라운드에서 실행되는 특징이 있다. 일반적으로 오래 실행되고 계산양이 많은 작업들에 적합하고, 병렬적으로 처리하거나 순차적으로 처리해야하는 작업들에 모두 이용 가능하다. 다양한 방식으로 배치 처리를 시작할 수 있는데, 예를 들면 ad-hoc, 스케줄, on-demand 방식으로 실행될 수 있다. 배치 애플리케이션들의 로깅, 체크포인팅, 병렬화 등에 대한 요구를 만족시키고, 배치 워크로드에 대해 관제할 수 있는 기능이 제공되어 작업의 게시, 중지/재시작같은 상호작용을 가능하게 한다.
JEUS에서는 스펙에서 제공하는 RI를 기반으로 동작하고, 작업이 실행될 스레드 풀을 사용자가 명세할 수 있도록 기능을 제공한다.
JBatch에 대한 상세한 내용은 JSR-352 스펙을 참조하기를 권한다.
본 절에서는 JBatch에서 정의하고 있는 작업을 구성하는 요소들에 대한 개념을 간략하게 설명한다.
다음은 JBatch를 구성하는 핵심적인 요소들 간의 관계이다.
사용자 애플리케이션에서 JobOperator를 통해서 Job을 제어(실행 혹은 중지)하고, Job은 수행할 작업에 대한 명세를 가지고 있다. Step은 Job의 하위에 속하며, 하나의 Job은 여러 Step으로 이루어질 수 있다. 마지막으로 step은 ItemReader, ItemProcessor, ItemWriter의 처리 모듈이 분리되어 있다. 이 모든 것은 JBatch 구현체 내의 JobRepository에 메타정보가 저장된다.
JobOperator는 Job의 처리에 대한 모든 상태 정보를 조회/제어할 수 있는 기능을 제공하고, 사용자 애플리케이션에서 배치 작업을 실행시켜주는 인터페이스 역할을 수행한다. 구체적으로 예를 들면 시작, 재시작, 종료에 대한 제어와 StepExecution을 가지고 오는 명령을 제공하기도 한다.
Job은 배치 프로세스 전체를 캡슐화한 정보이고 여러 Step으로 구성될 수 있고, 모든 Step을 전역적으로 관리하는 환경 설정을 할 수 있다. Job의 환경설정으로 Job의 이름, Step의 순서 정의, Job의 재시작 기능 설정 유무를 명시할 수 있다.
JobInstance
JobInstance는 개념적인 Job의 실행을 의미한다.
매일 특정 시간에 동작하는 Job을 정의했다고 가정하면 이 Job은 매일 특정 시간에 동작한다는 논리적인 JobInstance 하나를 가지게 된다. 만약에 1월 1일에 해당 Job이 실행되었다가 실패한 경우 다음 날이 되었을 때 실패했던 1월 1일의 JobInstance를 다시 실행한다. 또한, 1월 2일의 JobInstance도 새롭게 생성하여 실행한다. 따라서 각 JobInstance는 여러 번 실행될 수 있고, 여러 번 실행되는 주체를 JobExecution라고 한다. 실행할 때마다 JobExecution 인스턴스를 새롭게 생성된다. 새로운 JobInstance를 사용한다는 것은 처음부터 Job을 실행하겠다는 것을 의미하고, 이미 정의된 JobInstance를 사용하는 것은 Checkpoint 가능으로 처리해야 할 부분부터 시작하는 것이다.
JobInstance 자체는 데이터에 대해서 무지하다. 데이터에 대한 책임은 데이터를 item 단위로 읽어들여서 적재하는 ItemReader 구현체에게 있다.
JobParameters
JobInstance를 다른 JobInstance와 구별하기 위해서는 JobParameters를 이용해야 한다. 물론 다른 JobInstance에 동일한 JobParameters를 사용할 수도 있다. 구체적으로 JobParameters는 Java SE에서 제공하는 java.util.Properties 클래스이다.
예를 들면 1월 1일의 JobInstance는 JobParameters로 "schedule.date = 2016/01/01"를 가지고, 1월 2일의 JobInstance는 JobParameters로 "schedule.date = 2016/01/02"를 가진다.
Job은 여러 JobInstance를 가질 수 있고, 각 JobInstance는 각자의 JobParameters로 구별될 수 있다. JobExecution는 하나의 JobInstance 내에서 여러 번 실행할 수 있다.
JobExecution
Job의 실행을 한 번 시도하는 도구이다. 실행의 결과는 성공 혹은 실패가 될 수 있다. JobInstance는 해당 JobExecution이 성공적으로 끝났을 때만 성공한 것으로 간주된다. 예를 들면 매일 특정 시간에 동작하는 Job을 정의했을 때 해당 Job을 실제로 실행하는 주체는 JobExecution이 된다. 만약 해당 1월 1일에 수행한 JobExecution이 실패한다면, 다음 날 수행하는 시간에 동일한 1월 1일에 생성된 JobInstance에서 새로운 JobExecution을 정의하여 다시 작업을 수행하게 된다.
Step은 연속된 단계로 처리해야 하는 배치 작업을 각 작업들 간에 관계를 독립적으로 캡슐화한 정보이다. 모든 Job은 여러 개의 Step으로 구성될 수 있고, Step은 실제 배치 처리에 대한 모든 정보와 제어하기 위해 필요한 정보들을 담고 있다. Step의 간단한 예로 파일을 읽어서 데이터베이스로 넣는 작업을 예로 들 수 있다. Job과 JobExecution의 관계처럼 Step도 StepExecution을 가지고 있다.
다음 그림은 Job과 Step의 관계를 나타낸다. Job과 Step이 one-to-many 관계이고, 각 step은 상황에 따라 StepExecution을 여러 번 만들어 실행시킬 수 있다. JobExecution과 StepExecution 관계 역시 one-to-many 관계이고, 하나의 Job이 상황에 따라 여러 StepExecution을 생성하여 실행하는 관계가 될 수 있다.
StepExecution
Step의 실행을 한 번 시도하는 도구이다. Step이 실행될 때마다 StepExecution이 생성이 된다. Step은 Chunk나 Batchlet으로 이루어지고, 서로 상호 배제(mutually exclusive)한 관계로 동시에 사용될 수 없다.
Chunk
Chunk는 item-oriented한 배치 Step에 적절하며, reader-processor-writer 패턴으로 구현되고 트랜잭션의 scope으로 실행된다. 주기적으로 처리 과정을 Checkpoint로 기록한 후 새로운 트랜잭션을 실행한다.
다음은 chunk 기반의 item을 reader-processor-writer 패턴으로 처리하는 과정을 간략하게 나타낸 그림이다.
Step이 시작되면 ItemReader에서 입력 데이터를 item 단위로 읽어들여 ItemProcessor에게 넘겨주고, ItemProcessor는 각 item을 처리한다. 이 과정을 반복하다가 처리 완료된 item 리스트를 ItemWriter에게 전달한다.
Batchlet
Batchlet은 task-oriented한 배치 Step을 의미하고, item-oriented하지 않은 작업에 유용하게 이용될 수 있다. 구체적으로 예를 들면 파일을 전송하는 작업이나 특정 명령을 실행하는 작업 등이 있다.
ItemReader는 한 스텝을 구성하는 item들을 읽어들이는 도구이다. 읽어들인 item을 ItemProcessor에게 하나의 item씩 전달해주는 역할을 한다. 간단한 예로 파일을 열어서 처리해야 하는 작업이 있고 하나의 라인이 레코드 단위로 의미가 있다고 할 때, item을 하나의 라인으로 정의하여 전달할 수 있다.
뒤에서 설명할 Checkpoint 기능을 ItemReader에서 사용할 수 있는데 마지막에 성공적으로 읽어진 위치를 Checkpoint로 기록을 해놓으면, 오류가 나거나 재시작이 되었을 때 Checkpoint부터 다시 시작할 수 있다. 위에서 예로든 파일 내의 레코드의 경우 마지막으로 읽어들여진 라인수를 Checkpoint로 기록해 놓을 수 있다.
ItemWriter는 한 chunk에 대한 결과물을 출력하는 도구이다. 현재 들어온 item에 대한 정보만 알 수 있으며, 다음에 들어올 입력에 대한 정보는 알 수가 없다. ItemProcessor를 거친 여러 items들이 리스트로 전달될 수 있다.
ItemProcessor는 Item을 실제 처리하는 도구이다. ItemReader가 하나의 item을 읽어 온 것을 ItemProcessor가 처리하는 로직을 수행하게 되고, 처리 결과를 ItemWriter가 출력하는 역할을 담당한다. 배치 애플리케이션 개발자는 Item에 대한 처리 로직을 ItemProcessor에 구현해서 수행한다.
아주 오랜시간 동안의 처리시간이 필요한 거대한 데이터에 대한 배치 애플리케이션은 일반적으로 checkpoint/restart 기능을 요구한다.
Checkpoint는 StepExecution의 진행상태를 주기적으로 bookmark하여 나중에 재시작될 때 기록된 마지막 Checkpoint 부분부터 실행할 수 있도록 해주는 기능이다. Checkpoint를 할 때 lock을 잡고 기록하기 때문에 과도하게 Checkpoint 기능이 자주 동작할수록 전체 시스템의 성능에 크게 영향을 줄 수 있다. 따라서, Checkpoint 주기를 적절하게 설정하는 것은 성능에 중요한 튜닝 포인트이다.