스프링 배치 공식 레퍼런스를 한글로 번역한 문서입니다.
전체 목차는 여기에 있습니다.
목차
- 12.1. General Notes about Spring Batch and JSR-352
- 12.2. Setup
- 12.3. Dependency Injection
- 12.4. Batch Properties
- 12.5. Processing Models
- 12.6. Running a job
- 12.7. Contexts
- 12.8. Step Flow
- 12.9. Scaling a JSR-352 batch job
- 12.10. Testing
스프링 배치 3.0부터 JSR-352 규격을 전부 지원한다. 이번 장에서는 규격 명세를 설명하진 않으며, JSR-352 컨셉을 스프링 배치에 적용하는 법을 설명한다. JSR-352에 대한 추가 정보는 여기 JCP를 참고하라: https://jcp.org/en/jsr/detail?id=352
12.1. General Notes about Spring Batch and JSR-352
스프링 배치와 JSR-352는 구조적으로 동일하다.
둘 다 job으로 step을 구성하며, reader, processor, writer, listener를 사용한다.
그러나 상호 작용이 미묘하게 다르다.
예를 들어 스프링 배치의
org.springframework.batch.core.SkipListener#onSkipInWrite(S item, Throwable t)
는 스킵할 아이템과 스킵을 유발한 Exception
을 파라미터로 받는다.
같은 JSR-352 버전 메소드
(javax.batch.api.chunk.listener.SkipWriteListener#onSkipWriteItem(List<Object> items, Exception ex))
도 파라미터를 두 개를 받지만,
현재 청크 안에 있는 모든 아이템 List
와 스킵을 유발한 Exception
을 받는다.
이 차이로 인해 스프링 배치는
전통적인 스프링 배치 job과 JSR-352 기반 job을 다른 방법으로 실행한다.
스프링 배치의 reader, writer 등의 아티팩트(artifact)는
JSR-352의 JSL로 설정한 job에서도 동작하며, JsrJobOperator
로 실행되지만
JSR-352 명세대로 동작한다.
JSR-352 인터페이스 규격을 지키지 않은 배치 아티팩트는
전통적인 스프링 job과 잘 동작하지 않는다는 점을 알아두는 게 좋다.
12.2. Setup
12.2.1. Application Contexts
스프링 배치에서 JSR-352 기반으로 job을 실행하면 어플리케이션 컨텍스트가 두 개 생긴다.
부모 컨텍스트는 JobRepository
, PlatformTransactionManager
등의
스프링 배치 구조와 관련된 빈이 등록돼 있고,
자식 컨텍스트는 실행할 job 설정을 가지고 있다.
부모 컨텍스트는 프레임워크가 제공하는 jsrBaseContext.xml로 정의한다.
이 컨텍스트는 JSR-352-BASE-CONTEXT
시스템 프로퍼티로 오버라이드할 수 있다.
JSR-352 프로세서는 기본 컨텍스트에 프로퍼티를 주입하는 등의 처리를 하지 않으므로 추가적으로 필요한 컴포넌트는 없다.
12.2.2. Launching a JSR-352 based job
JSR-352에 따르면 매우 간단하게 배치 job을 실행할 수 있어야 한다. 아래 코드만 있으면 배치 job을 실행할 수 있다.
JobOperator operator = BatchRuntime.getJobOperator();
jobOperator.start("myJob", new Properties());
매우 편리하지만, 내부에 많은 처리가 숨겨져 있다.
스프링 배치는 뒷단에서 기반 구조를 부트스트랩하며, 원한다면 재정의할 수 있다.
BatchRuntime.getJobOperator()
를 처음 호출하면 다음 내용들이 부트스트랩된다:
Bean Name | Default Configuration | Notes |
---|---|---|
dataSource | 설정된 값을 가지고 있는 Apache DBCP BasicDataSource. |
디폴트로 HSQLDB가 부트스트랩된다. |
transactionManager |
org.springframework. jdbc.datasource. DataSourceTransactionManager |
위에서 정의한 dataSource 빈을 참고한다. |
A Datasource initializer | batch.drop.script , batch.schema.script 프로퍼티로 받은 스크립트를 실행하기 위해 설정한다. 디폴트로 HSQLDB 스키마 스크립트를 실행한다. batch.data.source.init 프로퍼티로 이 기능을 끌 수 있다. |
|
jobRepository | JDBC 기반 SimpleJobRepository . |
이 JobRepository 는 이전에 언급한 데이터소스와 트랜잭션 매니저를 사용한다. batch.table.prefix 프로퍼티로 스키마 테이블 프리픽스를 변경할 수 있다 (디폴트는 BATCH_다). |
jobLauncher | org.springframework.batch. core.launch.support. SimpleJobLauncher |
job을 실행할 때 사용한다. |
batchJobOperator | org.springframework.batch. core.launch.support. SimpleJobOperator |
기능 대부분은 JsrJobOperator 가 감싸서 제공한다. |
jobExplorer | org.springframework.batch. core.explore.support. JobExplorerFactoryBean |
JsrJobOperator 의 lookup 기능을 처리하기 위해 사용한다. |
jobParametersConverter | org.springframework.batch. core.jsr. JsrJobParametersConverter |
JSR-352 전용 JobParametersConverter 구현체. |
jobRegistry | org.springframework.batch. core.configuration.support. MapJobRegistry |
SimpleJobOperator 가 사용한다. |
placeholderProperties | org.springframework.beans. factory.config. PropertyPlaceholderConfigure |
batch-${ENVIRONMENT:hsql}.properties 파일을 로드해서 위에서 언급한 프로퍼티를 설정한다. ENVIRONMENT는 스프링 배치가 지원하는 데이터베이스를 명시할 때 사용하는 시스템 프로퍼티다 (디폴트는 hsql). |
JSR-352 기반 job을 실행하려면 위에 있는 모든 빈이 필요하다. 커스텀 기능을 사용하려면 모두 오버라이드해야 한다.
12.3. Dependency Injection
JSR-352는 대부분이 스프링 배치 프로그래밍 기반이다. 예를 들어 명시적으로 의존성(dependency) 주입을 구현하지 않아도 DI가 적용된다. 스프링 배치는 JSR-352에 정의된 배치 아티팩트를 로딩하기 위한 세 가지 메소드를 모두 지원한다:
- Implementation Specific Loader - 스프링 배치는 스프링 기반이므로 JSR-352 배치 job 내에 스프링 의존성을 주입할 수 있다.
- Archive Loader - JSR-352는 논리적인 이름과 클래스 명을 매핑하는 batch.xml 파일을 정의한다. 이 파일을 사용하려면 /META-INF/ 디렉토리에 있어야 한다.
- Thread Context Class Loader - JSR-352에선 패키지명을 모두 포함한 클래스 이름으로 JSL 안에 있는 배치 아티팩트 구현체를 설정한다. 스프링 배치는 JSR-352 방식으로 설정한 job도 지원한다.
JSR-352 기반 배치 job에 스프링 의존성을 주입하려면 배치 아티팩트를 스프링 어플리케이션 컨텍스트 빈으로 등록해야 한다. 빈을 정의하고 나면 batch.xml에 있는 빈처럼 스프링 빈도 참조할 수 있다.
@Configuration
public class BatchConfiguration {
@Bean
public Batchlet fooBatchlet() {
FooBatchlet batchlet = new FooBatchlet();
batchlet.setProp("bar");
return batchlet;
}
}
스프링 컨텍스트는 다른 스프링 기반 어플리케이션에서처럼 JSR-352 job과도 잘 동작한다. JSR-352 job이 유일하게 다른 점은 컨텍스트 정의 진입점이 /META-INF/batch-jobs/에 정의된 job이라는 것이다.
thread context class loader를 사용하려면 레퍼런스로 패키지명을 모두 포함한 클래스 이름을 제공하기만 하면 된다. 이 방법이나 batch.xml을 사용하면 참조하는 클래스에 매개변수가 없는 생성자가 있어야 빈을 생성할 수 있다는 점에 주의하라.
<?xml version="1.0" encoding="UTF-8"?>
<job id="fooJob" xmlns="http://xmlns.jcp.org/xml/ns/javaee" version="1.0">
<step id="step1" >
<batchlet ref="io.spring.FooBatchlet" />
</step>
</job>
12.4. Batch Properties
12.4.1. Property Support
JSR-352에 따르면 JSL 설정으로 job, step, 배치 아티팩트 레벨에 프로퍼티를 정의할 수 있어야 한다. 배치 프로퍼티는 각 레벨에 아래처럼 설정한다:
<properties>
<property name="propertyName1" value="propertyValue1"/>
<property name="propertyName2" value="propertyValue2"/>
</properties>
Properties
는 어떤 배치 아티팩트에도 설정할 수 있다.
12.4.2. @BatchProperty annotation
배치 아티팩트 클래스 필드에 @BatchProperty
, @Inject
어노테이션을 선언하면
Properties
를 참조할 수 있다 (스펙대로 두 가지 모두 사용해야 한다).
JSR-352가 정의하는 대로 프로퍼티를 사용하는 필드는 String이어야 한다.
타입 변환은 개발자가 구현해야 한다.
위에서 설명한 프로퍼티 블록을 설정하면 다음과 같이
javax.batch.api.chunk.ItemReader
아티팩트에서 접근할 수 있다:
public class MyItemReader extends AbstractItemReader {
@Inject
@BatchProperty
private String propertyName1;
...
}
필드 “propertyName1”의 값은 “propertyValue1”이 된다.
12.4.3. Property Substitution
몇 가지 오퍼레이터와 간단한 조건 포현식으로 속성값을 치환할 수 있다.
일반적으로 #{operator['key']}
같이 사용한다.
지원하는 오퍼레이터:
- jobParameters - job이 시작/재시작될 때 사용하는 job 파라미터 값에 접근한다.
- jobProperties - JSL에 job 레벨로 설정한 프로퍼티에 접근한다.
- systemProperties - 이름을 지정한 시스템 프로퍼티에 접근한다.
- partitionPlan - 파티션된 step의 파티션 플랜에서 이름을 지정한 프로퍼티에 접근한다.
#{jobParameters['unresolving.prop']}?:#{systemProperties['file.separator']}
왼쪽이 사용하려는 값이고, 오른쪽에 있는 값은 디폴트 값이다.
이 예시에서 #{jobParameters['unresolving.prop']}
를 매핑할 수 없다고 가정하면
file.separator
시스템 프로퍼티로 리졸브한다.
두 표현식을 모두 찾지 못하면 빈 문자열을 리턴한다.
‘;’로 구분해서 여러 조건을 사용할 수도 있다.
12.5. Processing Models
JSR-352는 스프링과 동일한 두 가지 기본 프로세싱 모델을 제공한다:
- 아이템 기반 처리 -
javax.batch.api.chunk.ItemReader
,javax.batch.api.chunk.ItemProcessor
(필수는 아니다),javax.batch.api.chunk.ItemWriter
를 사용. - 태스크 기반 처리 -
javax.batch.api.Batchlet
구현체 사용. 이 프로세싱 모델은org.springframework.batch.core.step.tasklet.Tasklet
기반 처리와 동일하다.
12.5.1. Item based processing
아이템 기반 처리는 ItemReader
가 읽을 아이템 수를 청크 사이즈로 설정한다.
이 방법으로 step을 만들려면 item-count
(디폴트는 10)를 명시해야 하고,
원한다면 checkpoint-policy
를 item으로 등록할 수 있다(이게 디폴트다).
...
<step id="step1">
<chunk checkpoint-policy="item" item-count="3">
<reader ref="fooReader"/>
<processor ref="fooProcessor"/>
<writer ref="fooWriter"/>
</chunk>
</step>
...
아이템 기반 체크포인팅은 time-limit
속성을 추가로 지원한다.
이 값은 지정된 아이템 수를 처리하는 데 걸리는 시간 제한한다.
타임아웃되면 item-count
를 몇으로 설정했는지는 상관없이
읽은 만큼만 청크로 처리한다.
12.5.2. Custom checkpointing
JSR-352에선 step을 커밋 인터벌 단위로 처리하는 것을 “checkpointing”이라고 한다.
아이템 기반 체크포인팅은 위에서 언급한 방법이다.
하지만 좀 더 견고한 방법이 필요할 때도 많다.
이 때문에 스펙에선
javax.batch.api.chunk.CheckpointAlgorithm
인터페이스를 구현해서
커스텀 체크포인팅 알고리즘을 만드는 것을 허용한다.
이 기능은 스프링 배치의 커스텀 completion policy와 동일하다.
CheckpointAlgorithm
구현체를 사용하려면
아래처럼 step에 커스텀 checkpoint-policy
를 설정하면 된다.
아래에선 fooCheckpointer
가 CheckpointAlgorithm
구현체를 참조한다.
...
<step id="step1">
<chunk checkpoint-policy="custom">
<checkpoint-algorithm ref="fooCheckpointer"/>
<reader ref="fooReader"/>
<processor ref="fooProcessor"/>
<writer ref="fooWriter"/>
</chunk>
</step>
...
12.6. Running a job
JSR-352 기반 job은 javax.batch.operations.JobOperator
가 시작한다.
스프링 배치는 이 인터페이스 구현체를 제공한다
(org.springframework.batch.core.jsr.launch.JsrJobOperator
).
javax.batch.runtime.BatchRuntime
이 이 구현체를 로딩한다.
JSR-352 배치 job을 시작하는 코드는 다음과 같다:
JobOperator jobOperator = BatchRuntime.getJobOperator();
long jobExecutionId = jobOperator.start("fooJob", new Properties());
위 코드는 다음과 같은 일을 한다:
- 기반
ApplicationContext
부트스트랩 - 배치 기능을 제공하려면 프레임워크에서 몇 가지 기반 구조를 부트스트랩해야 한다. JVM 당 한 번만 일어난다. 부트스트랩하는 컴포넌트는@EnableBatchProcessing
이 제공하는 컴포넌트와 유사하다. 자세한 내용은JsrJobOperator
의 javadoc을 참조하라. - job에서 필요한
ApplicationContext
로딩 - 위 예제는 프레임워크가 /META-INF/batch-jobs에서 fooJob.xml 파일을 찾아 전에 언급한 공유 컨텍스트의 자식 컨텍스트를 로드한다. - job 실행 - 컨텍스트 안에 정의된 job을 비동기로 실행한다.
이때
JobExecution
id를 리턴한다.
모든 JSR-352 기반 배치 잡은 비동기로 실행된다.
SimpleJobOperator
로 JobOperator#start
를 호출하면
스프링 배치는 첫 번째 실행인지 이전에 실행했던, 이전 실행을 재시도하는 건지 판단한다.
JSR-352 기반 JobOperator#start(String jobXMLName, Properties jobParameters)
를 사용하면
프레임워크는 항상 새 JobInstance를 만든다
(JSR-352는 job 파라미터로 job을 식별하지 않는다).
job을 재시작하고 싶으면 JobOperator#restart(long executionId, Properties restartParameters)
를 호출해야 한다.
12.7. Contexts
JSR-352는 job, step의 메타 데이터에 접근하기 위한 두 가지 컨텍스트 객체를 정의한다:
javax.batch.runtime.context.JobContext
, javax.batch.runtime.context.StepContext
.
step 레벨 아티팩트(Batchlet
, ItemReader
등)에서는 둘 다 사용할 수 있고
JobContext
는 job 레벨 아티팩트(e.g. JobListener
)에서 사용할 수 있다.
현재 스코프에서 JobContext
나 StepContext
를 참조하고 싶으면
간단하게 @Inject
어노테이션을 사용하면 된다:
@Inject
JobContext jobContext;
스프링의 @Autowire를 사용하면 이 컨텍스트를 주입할 수 없다.
스프링 배치에서는 JobContext
와 StepContext
가 각 실행 객체를 감싸고 있다
(JobExecution
, StepExecution
).
StepContext#setPersistentUserData(Serializable data)
로 저장한 데이터는
스프링 배치의 StepExecution#executionContext
에 저장된다.
12.8. Step Flow
step flow는 JSR-352 기반 job에서도 스프링 배치와 유사하게 동작한다. 하지만 몇 가지가 미묘하게 다르다:
- Decision도 step이다 -
전형적인 스프링 배치 job에선 decision은
독립적인
StepExecution
이 없는 상태일 뿐이며, step이 같는 권한이나 책임이 없다. 하지만 JSR-352에선 decision도 step이며 다른 step과 동일하게 동작한다 (StepExecution
을 가지는 등). 이 말은 재시작할 때도 다른 step과 동일하게 처리한다는 뜻이다. next
attribute 와 step transition - 전형적인 job은 이 두 개를 같은 step에서 같이 사용할 수 있다. JSR-352에서도 같이 사용할 수 있지만, next attribute를 우선시한다.- Transition element 적용 순서 - 표준 스프링 배치 job은 transition element를 구체적인 것부터 그렇지 않은 순서로 적용한다. JSR-352 job은 XML에 명시한 순서대로 적용한다.
12.9. Scaling a JSR-352 batch job
전통적인 스프링 배치 job은 4가지 방식으로 확장할 수 있다 (마지막 두 개는 여러 JVM에서 실행할 수 있다):
- Split - 여러 step을 병렬로 실행.
- Multiple threads - step 한 개를 여러 쓰레드에서 실행.
- Partitioning - 데이터를 나눠서 병렬 처리 (매니저/워커).
- Remote Chunking - 각 처리를 원격으로 실행.
JSR-352는 두 가지 확장법을 제공한다. 두 가지 모두 단일 JVM만 지원한다:
- Split - 스프링 배치와 동일
- Partitioning - 개념적으로는 스프링 배치와 동일하지만 구현이 약간 다르다.
12.9.1. Partitioning
JSR-352의 파티셔닝은 스프링 배치의 파티셔닝과 개념적으로 동일하다. 각 워커는 메타데이터로 처리할 입력을 식별하고 완료하면 매니저에게 결과를 보고한다. 하지만 몇 가지 중요한 차이점이 있다:
- 파티션된
Batchlet
- 설정한Batchlet
의 여러 인스턴스를 멀티 쓰레드에서 실행한다. 각 인스턴스는 JSL이나PartitionPlan
이 제공하는 각자의 프로퍼티 셋이 있다. PartitionPlan
- 스프링 배치 파티셔닝에선 각 파티션마다ExecutionContext
가 있다. JSR-352의 각 파티션은javax.batch.api.partition.PartitionPlan
하나와 메타 데이터를 담고 있는Properties
배열을 가지고 있다.PartitionMapper
- JSR-352는 두 가지 방법으로 파티션 메타 데이터를 만들 수 있다. 하나는 JSL를 통해 만드는 것이고(파티션 프로퍼티), 두 번째는javax.batch.api.partition.PartitionMapper
인터페이스를 구현하는 방식이다. 이 인터페이스는 파티셔닝 메타 데이터를 프로그래밍적으로 만드는 방법을 제공하는 스프링 배치의org.springframework.batch.core.partition.support.Partitioner
와 기능적으로 유사하다.StepExecutions
- 스프링 배치에선 파티션된 step을 매니저/워커로 실행한다. JSR-352에서도 같은 설정을 사용한다. 하지만 워커 step은 공식적인StepExecution
이 없다. 이 때문에JsrJobOperator#getStepExecutions(long jobExecutionId)
를 호출하면 매니저의StepExecution
만 반환된다.
job 레포지토리에 여전히
StepExecution
이 있으며JobExplorer
와 스프링 배치 어드민으로 접근할 수 있다.
- 보상 처리 - 스프링 배치는 매니저/워커 파티셔닝 로직을 step으로
구현하기 때문에 뭔가 잘못됐을 때
StepExecutionListener
로 보상 처리를 할 수 있다. 하지만 JSR-352 워커는 보상 처리를 제공하고 종료 코드를 동적으로 설정해 주는 다른 컴포넌트 컬렉션을 제공한다. 이 컴포넌트는 다음을 포함할 수 있다:
Artifact Interface | Description |
---|---|
javax.batch.api.partition.PartitionCollector |
워커 step이 매니저에게 정보를 전달할 수 있게 해준다. 워커 쓰레드 당 인스턴스가 하나씩 있다. |
javax.batch.api.partition.PartitionAnalyzer |
PartitionCollector 가 수집한 정보와 종료된 파티션의 상태를 받는 엔드포인트. |
javax.batch.api.partition.PartitionReducer |
파티션된 step이 보상 로직을 처리할 수 있게 해준다. |
12.10. Testing
JSR-352 기반 job은 모두 비동기로 실행하기 때문에
언제 job이 종료됐는지 알기 어렵다.
스프링 배치는 테스트를 도와줄 org.springframework.batch.test.JsrTestUtils
를 제공한다.
이 유틸리티 클래스는 job을 시작하고, 재시작하고, 완료를 기다리는 기능을 제공한다.
job이 완료되면 JobExecution
을 반환한다.
Next :Spring Batch Integration
스프링 배치 통합 한글 번역
전체 목차는 여기에 있습니다.