욱'S 노트

Spring Batch - Step 정의 본문

Programming/Spring Batch

Spring Batch - Step 정의

devsun 2015. 1. 23. 13:17

배치 도메인 중에서 step은 실제적인 배치 작업에서 필요한 모든 정보들이 정의된 도메인 오브젝트라고 했었다. Step은 배치 순서 중 독립적인 부분이다. 이것은 약간 작업을 작성하는 개발자의 재량에 따라 애매모호한 정의가 될 수 있다. Step은 개발자 원하는 것에 따라 단순하거나 복잡할 수가 있다. 파일로부터 데이터를 데이터베이스의 적재하는 것은 거의 코드가 필요없다. 더 복잡한 step은 어려운 비즈니스 룰을 포함할 수 있다.



Chunk-Oriented Processing


스프링 배치에서는 대부분의 공통적인 구현이 Chunk 기반 처리를 수행한다. Chunk 기반 처리는 데이터를 한번에 하나씩 읽어서 트랜잭션 경게내에서 출력을 위한 chunks들을 생성한다, 하나의 아이템은 ItemReader로부터 읽혀지고 ItemProcessor로 처리되고 모이게 된다. 입력된 아이템의 숫자가 commit interval에 도달하면 전체 chunk는 ItemWriter로 한꺼번에 전달되고 트랜잭션은 커밋된다.





Configuring a Step


Step은 기본적으로 몇개의 필수적인 의존성을 요구한다. 하지만 잠재적으로 많은 collaborator들을 포함할 수 있다.

<batch:job id="simpleJob" job-repository="jobRepository">
<batch:step id="simpleStep">
<batch:tasklet transaction-manager="transactionManager">
<batch:chunk reader="itemReader" writer="itemWriter" commit-interval="10"/>
</batch:tasklet>
</batch:step>
</batch:job>

위의 정의에 대한 내용은 다음과 같다.


- reader : ItemReader 

- writer : ItemWriter

- transaction-manager : 프로세싱 중 트랜잭션의 시작과 커밋을 담당할 트랜잭션 매니져

- commit-interval : 트랜잭션을 커밋하기 전에 처리될 아이템의 수


job-repository나 transaction-manager 속성을 정의하지 않으면 jobRepository, transactionManager라는 id를 가진 빈을 찾아서 인젝션한다.


Inheriting from a Parent Step


Step을 정의하다 보면 비슷한 정의가 발생할 경우, step들간에 공유를 할 수 있다. 이러할 경우 parent 정의 방식을 이용해 정의를 포함할 수 있다.

<batch:step id="parentStep">
<batch:tasklet allow-start-if-complete="true">
<batch:chunk reader="itemReader" writer="itemWriter" commit-interval="10"/>
</batch:tasklet>
</batch:step>
<batch:step id="concreteStep1" parent="parentStep">
<batch:tasklet >
<batch:chunk processor="itemProcessor" commit-interval="5"/>
</batch:tasklet>
</batch:step>

다음은 abstract step을 이용한 상속 방식이다.

<batch:step id="abstractParentStep" abstract="true">
<batch:tasklet>
<batch:chunk commit-interval="10"/>
</batch:tasklet>
</batch:step>
<batch:step id="concreteStep2" parent="abstractParentStep">
<batch:tasklet>
<batch:chunk reader="itemReader" writer="itemWriter"/>
</batch:tasklet>
</batch:step>

마지막으로 list를 merging한 설정이다.

<batch:step id="listenersParentStep" abstract="true">
<batch:listeners>
<batch:listener ref="listenerOne"/>
</batch:listeners>
</batch:step>
<batch:step id="concreteStep3" parent="listenersParentStep">
<batch:tasklet>
<batch:chunk reader="itemReader" writer="itemWriter" commit-interval="5"/>
</batch:tasklet>
<batch:listeners merge="true">
<batch:listener ref="listenerTwo"/>
</batch:listeners>
</batch:step>


The Commit Interval


위에 언급되었듯, Step은 items를 읽고 쓰는데 있어서 트랜잭션 매니져를 이용해 주기적으로 커밋을 수행한다. Commit-interval이 1이라면 하나의 아이템들은 개별적으로 커밋이 수행될 것이다. 그러나 많은 상황에서 트랜잭션을 열고 커밋을 수행하는 비용은 비싸다. 이러한 이유로 commit을 수행할 아이템의 숫자를 지정하는 설정이 제공된다. 


Configuring a Step for Restart 


많은 시나리오에서 당신은 step의 수행횟수를 컨트롤 하고 싶을 수 있다. 예를들어 특별한 step은 단지 한번만 수행되도록 설정을 할 필요가 있을 수 있다. 이러한 경우 start-limit 속성을 1로 지정하면 다시 수행을 시도했을 경우 Exception이 발생할 것이다.

<batch:step id="concreteStep1" parent="parentStep">
<tasklet start-limit="1">
<batch:chunk processor="itemProcessor" commit-interval="1"/>
</tasklet>
</batch:step>


Restarting a complete step


재시작이 가능한 작업에서 첫번째 수행에서 해당 스텝이 성공했을 경우 수행은 skip이 된다. 그러나 allow-start-if-complete 속성을 "true"로 변경하면 항상 수행이 된다.

<batch:step id="concreteStep1" parent="parentStep">
<batch:tasklet allow-start-if-complete="true">
<batch:chunk processor="itemProcessor" commit-interval="5"/>
</batch:tasklet>
</batch:step>


Configuring Skip Logic


많은 경우에 에러가 발생했을때, step이 실패하는 대신에 스킵할 수 있다. 이러한 결정에 데이터에 대한 이해가 있는 어떤 사람들에 의해서 결정된다. 예를들어 금융 데이터의 경우 전송된 돈과 결과가 정확하게 일치하여야 하기 때문에 스팁할 수 없다. 그러나 특정 벤더로부터 받은 데이터를 벌크 로딩하는 경우에는 스킵이 허용될 것이다.


<batch:step id="concreteStep1" parent="parentStep">
<batch:tasklet allow-start-if-complete="true">
<batch:chunk processor="itemProcessor" commit-interval="5" skip-limit="5">
<batch:skippable-exception-classes>
<batch:include class="org.springframework.batch.item.file.FlatFileParseException"/>
</batch:skippable-exception-classes>
</batch:chunk>
</batch:tasklet>
</batch:step>


위와 같이 설정하였다면 10번까지 FlatFileParseException에 대해서 다섯번까지 스킵이 허용되고, 만약 5번 넘게 FlatFileParseExceptin이 발생한다면 예외는 전달되고 step은 실패할 것이다.


Configuring Retry Logic


어떤 경우에는 재시도가 필요할 수도 있다. 네트웍이나 락발생 같은 경우에는 재시도로 일시적인 장애를 극복할 수 있다.


<batch:step id="concreteStep1" parent="parentStep">
<batch:tasklet allow-start-if-complete="true">
<batch:chunk processor="itemProcessor" commit-interval="5" retry-limit="5">
<batch:retryable-exception-classes>
<batch:include class="org.springframework.dao.DeadlockLoserDataAccessException"/>
</batch:retryable-exception-classes>
</batch:chunk>
</batch:tasklet>
</batch:step>

위의 설정의 경우 데드락이 발생했을 경우에 5번까지 재시도를 하고 그래도 데드락이 발생한다면 예외가 전달된다.


Controlling Rollback


재시도와 스킵을 고려하지 않는다면  ItemWriter로 부터 예외가 전달될 경우 step에서는 rollback을 수행할 것이다. 하지만 많은 경우 ItemWriter로부터 발생한 예외에서 롤백을 유발하지 않고 아무런 동작이 발생하지 않기를 원할 수도 있다. 이러한 이유로 step은 rollback을 유발하지 않기 위해 예외 리스트를 설정할 수 있다.

<batch:step id="concreteStep1" parent="parentStep">
<batch:tasklet allow-start-if-complete="true">
<batch:chunk processor="itemProcessor" commit-interval="5"/>
<batch:no-rollback-exception-classes>
<batch:include class="org.springframework.batch.item.validator.ValidationException"/>
</batch:no-rollback-exception-classes>
</batch:tasklet>
</batch:step>

Transactional Readers


기본적으로 ItemReader는 기본적인 형태는 forward only이다. Step은 reader로부터 다시 읽을 필요가 없기때문에 아이템의 롤백을 위해 입력을 버퍼링한다. 그러나 특정 시나리오 JMS queue와 같은 트랜잭션 리소스에서는 큐와 트랜잭션의 롤백이 결합되어 있다. 이런 경우 롤백이 되면 item은 다시 큐로 들어가야 한다. 이러한 이유로 step은 items을 버퍼링하지 않도록 설정할 수 있어야 한다.

<batch:chunk processor="itemProcessor" commit-interval="5" reader-transactional-queue="true"/>

Transaction Attributes


Step별로 트랜잭션 속성들을 정의할 수 있다.

<batch:chunk processor="itemProcessor" commit-interval="5"/>
<batch:transaction-attributes isolation="READ_UNCOMMITTED" propagation="REQUIRED" timeout="30"/>

Intercepting Step Execution


Job과 마찬가지로 step 수행 도중에 많은 이벤트들이 발생하고 해당하는 확장 포인트들이 제공된다. 이러한 클래스들은 StepListener의 확장으로 제공된다.

<batch:step id="concreteStep2" parent="abstractParentStep">
<batch:tasklet>
<batch:chunk reader="itemReader" writer="itemWriter"/>
<batch:listeners>
<batch:listener ref="sampleListener"/>
</batch:listeners>
</batch:tasklet>
</batch:step>

각 확장영역에 따라 StepExecutionListener, ChunkListener, ItemReadListener, ItemProcessListener, ItemWriteListener, SkipListener등이 제공된다.


Tasklet Step


Chunk 기반의 작업이 아닐경우에 tasklet에서 직접 빈을 참조하여 작업을 수행할 수 있다. 이런 경우 해당 빈은 Tasklet을 상속받아 구현을 해야한다. 다음은 Tasklet을 간단하게 설정한 예이다.

<batch:tasklet ref="sampleTasklet"/>

Late Binding of Job or Step Attributies


XML이나 Flat File 예제 모두 Spring 리소스 추상화를 통해 파일을 획득한다. 일반적인 경우 파일이 이름이 고정되어 있지 않을 경우가 많다. 이러한 경우 시스템 프로퍼티 및 작업 파라미터등을 통해 동적으로 해당 속성을 바인딩할 수 있다.

<bean id="flatFileItemReader" class="org.springframework.batch.item.file.FlatFileItemReader">
<property name="resource" value="${input.file}"/>
</bean>


작업 파라미터로 전달되었을 경우는 다음과 같이 표현된다.

<bean id="flatFileItemReader" class="org.springframework.batch.item.file.FlatFileItemReader">
<property name="resource" value="#{jobParameters[input.file.name]}"/>
</bean>


또 한가지 해당 속성들은 작업 혹은 스텝으로 범위를 지정할 수 있는데 다음과 같이 지정한다.

<bean id="flatFileItemReader" class="org.springframework.batch.item.file.FlatFileItemReader" scope="job"/>




Comments