출처 : http://willygwu2003.blog.me/130171432318

참고 : http://docs.spring.io/spring-batch/reference/html/

다음의 내용은 Spring Batch 2.0 Documentation의 중에 기본적인 내용을 요약한 것입니다.

 

1. Job Configuration 

 

1.1. Restartability

 

restartable='false'로 설정된 Job은 다시 재 실행 될 수 없습니다.

일반적으로 Job은 실패한 시점에서 다시 재 시작을 할 수 있으야 하나, 어떤 경우에는 어떤 시점에서 재 시작을 해야하는지 알 수 없는 경우가 있습니다. 이 경우에는 다음과 같이 설정하여 재시작을 원천적으로 막는 방법이 있습니다.

일반적으로, 재시작 할 수 없는 Job이 실패한 경우에는 관리자 또는 개발자에 의해 수동적으로 처리하게 됩니다.

<job id="footballJob" restartable="false">
    ...
</job>

1.2. JobExecutionListener

 

Job이 실행 lifecycle 이벤트를 받아 커스텀 로직을 정의할 필요가 있을때, 다음의 리스너를 등록하면 유용합니다.

Job이 성공 또는 실패에 상관없이 afterJob은 실행됩니다.

public interface JobExecutionListener {

    void beforeJob(JobExecution jobExecution);

    void afterJob(JobExecution jobExecution);

}
<job id="footballJob">
    <step id="playerload"          parent="s1" next="gameLoad"/>
    <step id="gameLoad"            parent="s2" next="playerSummarization"/>
    <step id="playerSummarization" parent="s3"/>
    <listeners>
        <listener ref="sampleListener"/>
    </listeners>
</job>

 

1.3. Configuring JobRepository

 

Database Repository 

 

<job-repository id="jobRepository"
    data-source="dataSource"
    transaction-manager="transactionManager"
    isolation-level-for-create="SERIALIZABLE"
    table-prefix="BATCH_"
 max-varchar-length="1000"
/>

 

In-Memory Repository

 

In-Memory Repository는 다음의 단점을 가지고 있음으로, 실제 Production 환경에서는 Database Repository가 주 설정 방법입니다.

 

- 재 시작된 경우 모든 상태 정보가 없어집니다.

- 같은 JobParameters 값을 가진 두개의 JobInstance가 동시에 실행된다는 보장이 없습니다.

- Multi-threaded Job 환경에서는 적합하지 않습니다.

<bean id="jobRepository" 
  class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean">
    <property name="transactionManager" ref="transactionManager"/>
</bean>

 

2. Step Configuration 

 

2.1. Commit Interval

 

다음은 각 트랜잭션에서 10개의 아이템이 처리되도록 설정하였습니다. 트랜잭션이 시작되면, ItemReader가 아이템을 일고, readCount가 10이되면 ItemWriter에 아이템 리스트가 전달됩니다. 그리고 트랜잭션이 컴밋됩니다.

commit-interval='10' 설정에 의해 'Chunk' 프로세스 단위가 결정됩니다.

<job id="sampleJob">
    <step id="step1">
        <tasklet>
            <chunk reader="itemReader" writer="itemWriter" commit-interval="10"/>
        </tasklet>
    </step>
</job>

 

2.2. Configuring Step Restart

 

'start-limit'은 Step이 실패 이후 재식작 가능한 수를 통제합니다. 다음의 예에서 Step은 단 한번 실행될 수 있고 다시 실행되면 exception이 발생하게 됩니다. default 값은 Interger.MAX_VALUE 입니다.

<step id="step1">
    <tasklet start-limit="1">
        <chunk reader="itemReader" writer="itemWriter" commit-interval="10"/>
    </tasklet>
</step>

 재시작 가능한 Job에서는, 처음의 Step 실행에서 성공 또는 실패에 상관없이 항상 재 실행이 될 필요가 있습니다. 디폴트로 'COMPLETED' 상태코드의 Step은 재 실행되더라도 Skip이 됩니다.

하지만 어떤 경우에는 이미 성공한 Step이라도 다시 실행 될 필요가 있는데, 이 경우에 다음과 같이 allow-start-if-complete='true'로 설정하면 됩니다.

 

<step id="step1">
    <tasklet allow-start-if-complete="true">
        <chunk reader="itemReader" writer="itemWriter" commit-interval="10"/>
    </tasklet>
</step>

2.3. Configuring Skip Logic

 

많은 경우에 에러가 발생하였을 경우 Step이 실패하는 것이 아니라, 단순히 Skip하고 계속 진행 될 필요가 있습니다.

다음의 예는 FlatFileReader가 사용되었고 프로세스 중에 FlatFileParseException이 발행한 아이템은 Skip되도록 설정되었습니다.

또한 skip-limit=10 설정에 의해 skipCount가 10에 도달하면 해당 step은 실패하게 됩니다.

<step id="step1">
   <tasklet>
      <chunk reader="flatFileItemReader" writer="itemWriter" 
             commit-interval="10" skip-limit="10">
         <skippable-exception-classes>
            <include class="org.springframework.batch.item.file.FlatFileParseException"/>
         </skippable-exception-classes>
      </chunk>
   </tasklet>
</step>

다음은 Skippable한 Exception 처리와 그렇지 못한 Exception을 설정한 예입니다.

<include> 태그의 Exception은 Skip이 가능한 Exception이며, <exclude> 태그안의 Exception이 발생한 경우에는 Step이 실패하게 됩니다.

<step id="step1">
    <tasklet>
        <chunk reader="flatFileItemReader" writer="itemWriter" 
               commit-interval="10" skip-limit="10">
            <skippable-exception-classes>
                <include class="java.lang.Exception"/>
                <exclude class="java.io.FileNotFoundException"/>
            </skippable-exception-classes>
        </chunk>
    </tasklet>
</step>

 

2.4. Configuring Retry Logic

 

단순이 Exception이 발생한 경우은 회복가능한 Exception가 불가능한 Exception이 있습니다.

다음과 같이 데이터베이스 LOCK에 의해 실패한 경우에는 해당 아이템에 대한 처리를 Skip하는 것이 아니라 잠시 이후 다시 처리하면 성공할 확율이 높습니다. 이러한 경우에 다음과 같은 설정이 유용합니다.

 

<step id="step1">
   <tasklet>
      <chunk reader="itemReader" writer="itemWriter" 
             commit-interval="2" retry-limit="3">
         <retryable-exception-classes>
            <include class="org.springframework.dao.DeadlockLoserDataAccessException"/>
         </retryable-exception-classes>
      </chunk>
   </tasklet>
</step>

 

2.5. Controlling Rollback

 

일반적으로 ItemWriter의 포르세스 중에 Exception이 발생한 경우에는 Rollback이 발생합니다.

하지만, 특정 시나리오에서는 롤백이 발생하지 않기를 원하는 경우가 있습니다. 이런 경우 <no-rollback-exception-classes> 설정이 유용합니다.

<step id="step1">
   <tasklet>
      <chunk reader="itemReader" writer="itemWriter" commit-interval="2"/>
      <no-rollback-exception-classes>
         <include class="org.springframework.batch.item.validator.ValidationException"/>
      </no-rollback-exception-classes>
   </tasklet>
</step>

 

 ItemReader는 기본적으로 읽어온 아이템을 버퍼에 저장합니다다. 따라서, rollback이 발생하더라도 다시 아이템을 ItemReader로 부터러 읽어 올 필요가 없습니다. 하지만 , 아이템을 JMS Queue로 부터 읽어 온 경우에는 rollback이 발생하면 아이템을 다시 Queue에서 읽어와야 합니다. 즉 버퍼를 사용하지 않도록 설정하는 요소가 is-reader-transactional-queue='true' 입니다.

 

<step id="step1">
    <tasklet>
        <chunk reader="itemReader" writer="itemWriter" commit-interval="2"
               is-reader-transactional-queue="true"/>
    </tasklet>
</step>

 

다음과 같이 트랜잭션 isolation, propagation, timeout 셋팅이 Step 레벨에서 가능합니다.

<step id="step1">
    <tasklet>
        <chunk reader="itemReader" writer="itemWriter" commit-interval="2"/>
        <transaction-attributes isolation="DEFAULT" 
                                propagation="REQUIRED" 
                                timeout="30"/>
    </tasklet>
</step>

 

2.6. Registering ItemStreams with Step

 

Step이 실패한 경우 재 시작할 필요가 있는데 이 경우 Repository로 부터 Step의 StepExecution 메티 정보를 참조하게 됩니다.

ItemStreams 인터페이스를 구현한 ItemReader, ItemProcessor, ItemWriter는 주기적으로 Step의 상태 정보를 저장 및 업데이트하게 됩니다.

하지만 커스텀 reader, processor, writer은 다음과 같이 명시적으로 ItemStreams로 등록되어야 합니다.

<step id="step1">
    <tasklet>
        <chunk reader="itemReader" writer="compositeWriter" commit-interval="2">
            <streams>
                <stream ref="fileItemWriter1"/>
                <stream ref="fileItemWriter2"/>
            </streams>
        </chunk>
    </tasklet>
</step>

<beans:bean id="compositeWriter" 
            class="org.springframework.batch.item.support.CompositeItemWriter">
    <beans:property name="delegates">
        <beans:list>
            <beans:ref bean="fileItemWriter1" />
            <beans:ref bean="fileItemWriter2" />
        </beans:list>
    </beans:property>
</beans:bean>

 

2.7. Configuring Listener

 

Step에 Listener 설정하는 방법

<step id="step1">
    <tasklet>
        <chunk reader="reader" writer="writer" commit-interval="10"/>
        <listeners>
            <listener ref="chunkListener"/>
        </listeners>
    </tasklet>
</step>

StepExecutionListener

 

Step Execution의 가장 일반적인 리스너로써, Step이 시작하기 전, 종료 이후 Notification을 처리 할 수 있습니다.

주로 정상적으로 종료되었는지 아니면 실패하였는지에 대한 후 처리를 위해 사용됩니다.

public interface StepExecutionListener extends StepListener {

    void beforeStep(StepExecution stepExecution);

    ExitStatus afterStep(StepExecution stepExecution);

}

 

ChunkListener

 

특정 트랜잭션안에서 'chunk' 프로세스 시작전과 완류 이후에 어떤 로직을 수행하는데 도움이 되는 리스너입니다.

public interface ChunkListener extends StepListener {

    void beforeChunk();

    void afterChunk();

}

 

ItemReadListener

 

ItemReader에 의해 아이템 읽기 프로세스 중에 에러가 발생한 경우, 로그와 같은 후 처리 하기에 유용한 리스너입니다.

public interface ItemReadListener<T> extends StepListener {
  
    void beforeRead();

    void afterRead(T item);
    
    void onReadError(Exception ex);

}

 

ItemProcessListener

 

ItemProcessor에 의해 아이템 비즈니스 로직 프로세스 중에 에러가 발생한 경우, 로그와 같은 후 처리 하기에 유용한 리스너입니다.

public interface ItemProcessListener<T, S> extends StepListener {

    void beforeProcess(T item);

    void afterProcess(T item, S result);

    void onProcessError(T item, Exception e);

}

 

ItemWriteListener 

 

ItemWriter에 의해 아이템 쓰기 프로세스 중에 에러가 발생한 경우, 로그와 같은 후 처리 하기에 유용한 리스너입니다.

 public interface ItemWriteListener<S> extends StepListener {

    void beforeWrite(List<? extends S> items);

    void afterWrite(List<? extends S> items);

    void onWriteError(Exception exception, List<? extends S> items);

}

 

SkipListener

 

ItemReader, ItemProcessor, ItemWriter 프로세스 중에 Skip된 아이템을 추적하는데 유용한 리스너입니다.

주로 Skip된 아이템을 로깅하는데 사용됩니다.

상기의 'Configuring Skip Logic'을 참조 바랍니다.

 

public interface SkipListener<T,S> extends StepListener {

    void onSkipInRead(Throwable t);

    void onSkipInProcess(T item, Throwable t);

    void onSkipInWrite(S item, Throwable t);

}

 

 

2.8 Controller Step Flow

 

Sequential Flow

 

'Step' 엘리먼트의 'next' 에트리뷰트를 적용함으로써 순차적인 Step 흐름을 설정할 수 있습니다.

단점으로는, 만약 stepA가 실패하면 전체 Job이 실패하게 됩니다.

<job id="job">
    <step id="stepA" parent="s1" next="stepB" />
    <step id="stepB" parent="s2" next="stepC"/>
    <step id="stepC" parent="s3" />
</job>

Conditional Flow 

 

다음의 조건적인 Step 흐름 설정으로 stepA의 'ExitStatus'가 'FAILED'인 경우 stepC로 이동하며, 그 외의 'EixtStatus' 결과값은 stepB로 그 흐름이 이동함을 말합니다.

<job id="job">
    <step id="stepA" parent="s1">
        <next on="*" to="stepB" />
        <next on="FAILED" to="stepC" />
    </step>
    <step id="stepB" parent="s2" next="stepC" />
    <step id="stepC" parent="s3" />
</job>
상기의 ExitStatus는 BatchStatus가 아님을 주목하시길 바랍니다. BatchStatus는 Job 및 Step의 상태를 기록하기 위한 JobExecution 및 StepExecutio의 property로, 그 값은 다음과 같습니다. - COMPLETED, STARTING, STARTED, STOPPING, STOPPED, FAILED, ABANDONED 또는 UNKNOWN. 반면, ExitStatus는 Step이 실행 종료 이후의 상태를 나타내는 코드로 다음 step으로 이동할 것인지 여기서 Job을 멈출 것인지를 판단하는 코드입니다.
public class SkipCheckingListener extends StepExecutionListenerSupport {

    public ExitStatus afterStep(StepExecution stepExecution) {
        String exitCode = stepExecution.getExitStatus().getExitCode();
        if (!exitCode.equals(ExitStatus.FAILED.getExitCode()) && 
              stepExecution.getSkipCount() > 0) {
            return new ExitStatus("COMPLETED WITH SKIPS");
        }
        else {
            return null;
        }
    }

}


+ Recent posts