토리맘의 한글라이즈 프로젝트 logo 토리맘의 한글라이즈 프로젝트

스프링 배치 공식 레퍼런스를 한글로 번역한 문서입니다.

전체 목차는 여기에 있습니다.

목차


8.1. RepeatTemplate

배치 처리는 간단한 최적화에서든 job 일부로써든, 반복적인 처리를 수행한다. 반복처리를 전략화하고 일반화하기 위해, 스프링 배치는 프레임워크에 얼마나 반복해야 하는지 알려줄 수 있는 RepeatOperations 인터페이스를 제공한다. RepeatOperations 인터페이스 정의는 다음과 같다:

public interface RepeatOperations {

    RepeatStatus iterate(RepeatCallback callback) throws RepeatException;

}

callback은 반복할 비지니스 로직을 넣을 수 있는 인터페이스로, 인터페이스 정의는 다음과 같다:

public interface RepeatCallback {

    RepeatStatus doInIteration(RepeatContext context) throws Exception;

}

구현체가 반복을 종료할 때까지 콜백을 반복해서 호출한다. 이 인터페이스는 RepeatStatus.CONTINUABLERepeatStatus.FINISHED를 포함하는 열거형(enumeration)을 리턴한다. RepeatStatus는 더 할 일이 남아 있는지에 관한 정보를 준다. 일반적으로 RepeatOperations 구현체는 RepeatStatus를 검사해서 반복을 종료할지 결정한다. 호출자에게 더 이상 처리할 작업이 없다는 신호를 주려면 콜백에서 RepeatStatus.FINISHED를 리턴하면 된다.

아래 예제에 있는 RepeatTemplateRepeatOperations의 가장 간단한 구현체다:

RepeatTemplate template = new RepeatTemplate();

template.setCompletionPolicy(new SimpleCompletionPolicy(2));

template.iterate(new RepeatCallback() {

    public RepeatStatus doInIteration(RepeatContext context) {
        // Do stuff in batch...
        return RepeatStatus.CONTINUABLE;
    }

});

위 예제에서는 아직 처리할 작업이 남았다는 뜻으로 RepeatStatus.CONTINUABLE을 리턴한다. 호출자에게 더 이상 처리할 게 없다고 알려주고 싶으면 콜백에서 RepeatStatus.FINISHED를 리턴하면 된다. 콜백에서 수행하는 작업을 고려해서 반복을 종료하는 경우도 있다. 그렇지 않다면 위 예제에서처럼 콜백과 외부 policy에 결정을 위임하는 한 사실상 무한 루프나 다름없다.

8.1.1. RepeatContext

RepeatCallback 메소드는 RepeatContext를 파라미터로 받는다. 컨텍스트를 무시하는 콜백도 많지만, 원한다면 실행을 반복하는 동안 데이터를 일시적으로 저장할 수 있다. iterate 메소드가 종료되면 컨텍스트에 담긴 데이터는 사라진다.

반복 호출을 중첩해서 사용한다면 RepeatContext는 부모 컨텍스트를 가진다. iterate를 호출하는 동안 공유할 데이터를 부모 컨텍스트에 저장하는 경우도 있다. 예를 들어 반복 처리하는 동안 이벤트 발생 횟수를 세고 싶다면 다음 호출로 넘어가도 이 값을 가지고 있어야 한다.

8.1.2. RepeatStatus

RepeatStatus는 스프링 배치가 처리가 끝났는지 판별하기 위한 열거형(enumeration)이다. 가능한 RepeatStatus 값은 아래 표에 보이는 두 가지가 있다:

Table 18. RepeatStatus Properties

Value Description
CONTINUABLE 처리할 작업이 남아 있다.
FINISHED 더 이상 반복할 필요가 없다.

RepeatStatus 값은 안에 있는 and() 메소드를 사용해 논리적인 AND 연산으로 조합할 수 있다. 이를 통해 연속적인 플래그에 AND 연산을 적용할 수 있다. 즉, 둘 중 하나가 FINISHED라면 결과도 FINISHED다.


8.2. Completion Policies

RepeatTemplateiterate 메소드 안에서 반복을 중단할지는 RepeatContext의 팩토리이기도한 CompletionPolicy가 결정한다. RepeatTemplate의 책임은 현재 policy로 RepeatContext를 생성해 매번 반복할 때마다 RepeatCallback에 전달하는 것이다. 콜백이 doInIteration 메소드를 완료하면 RepeatTemplateCompletionPolicy를 호출해 상태(RepeatContext에 저장돼 있는)를 업데이트하라고 요청해야 한다. 그다음 policy에게 반복을 종료할지 물어본다.

스프링 배치는 간단한 CompletionPolicy 구현체를 제공한다. SimpleCompletionPolicy는 고정된 횟수만큼만 반복하게 만든다 (RepeatStatus.FINISHED를 사용하면 언제든지 조기 종료할 수 있다).

좀 더 세분화해서 결정하려면 자체 completion policy를 구현해야 한다. 예를 들어 커스텀 custom policy로 온라인 시스템이 정상화되면 배치 job을 실행하지 않게 만들 수 있다.


8.3. Exception Handling

RepeatCallback 안에서 예외가 발생하면 RepeatTemplateExceptionHandler를 참고해서 예외를 다시 던질지 말지 결정한다.

ExceptionHandler 인터페이스 정의는 다음과 같다:

public interface ExceptionHandler {

    void handleException(RepeatContext context, Throwable throwable)
        throws Throwable;

}

보통은 주어진 타입의 exception 발행 횟수를 계산해서 limit에 도달할 때 실패하는 식으로 사용한다. 스프링 배치는 SimpleLimitExceptionHandler와 좀 더 유연한 RethrowOnThresholdExceptionHandler를 제공한다. SimpleLimitExceptionHandler는 limit, exception 타입 프로퍼티가 있어서 exception이 발생할 때마다 타입을 비교한다. 주어진 exception 타입의 모든 하위 클래스도 함께 카운팅한다. 주어진 모든 exception 타입은 계속 무시하다가 limit에 도달하면 예외를 다시 던진다. 다른 exception 타입은 전부 다시 던진다.

SimpleLimitExceptionHandler에서 선택적으로 사용할 수 있는 중요한 프로퍼티가 있는데, boolean 타입의 useParent다. 디폴트는 false이므로 limit은 현재 RepeatContext 안에서만 사용된다. true로 설정하면 중첩된 반복 처리(step 안에 있는 청크 셋같이) 안에 있는 형제 컨텍스트와 limit을 공유한다.


8.4. Listeners

가끔은 반복 처리할 때마다 횡단 관심사(cross cutting concerns)에서도 추가로 콜백을 받고 싶을 수 있다. 스프링 배치는 이를 위한 RepeatListener 인터페이스를 지원한다. RepeatTemplateRepeatListener를 등록하면 매번 RepeatContext, RepeatStatus와 함께 콜백 받을 수 있다.

RepeatListener 인터페이스 정의는 다음과 같다:

public interface RepeatListener {
    void before(RepeatContext context);
    void after(RepeatContext context, RepeatStatus result);
    void open(RepeatContext context);
    void onError(RepeatContext context, Throwable e);
    void close(RepeatContext context);
}

모든 반복 전후에 open, close로 콜백 받는다. before, after, onErrorRepeatCallback을 사용 할 때마다 호출한다.

리스너가 둘 이상이라면 리스트 안에 저장하므로 순서가 정해져 있다. 이때는 open, before 메소드는 동일한 순서로 호출하는 반면 after, onError, close는 역순으로 호출한다.


8.5. Parallel Processing

RepeatOperations 구현체가 항상 콜백을 순차적으로 실행해야 한다는 법은 없다. 콜백을 병렬 실행하는 구현체도 있다. 스프링은 이를 위해 스프링의 TaskExecutor 전략을 사용해 RepeatCallback을 실행하는 TaskExecutorRepeatTemplate을 제공한다. 디폴트는 SynchronousTaskExecutor를 사용하도록 되어 있어서, 동일한 쓰레드로 전체 반복 처리를 실행하면(일반 RepeatTemplate처럼) 문제가 생길 수 있다.


8.6. Declarative Iteration

때로는 매번 반복해야 하는 비지니스 로직도 있다. 메세지 파이프라인 최적화가 가장 대표적인 예시다. 메세지가 반복적으로 도착한다면, 메세지 마다 별도로 트랜잭션 비용을 감수하기보다 배치로 처리하는 게 더 효율적이다. 스프링 배치는 이럴 때 사용할, RepeatOperations의 메소드 호출을 감싸고 있는 AOP 인터셉터를 지원한다. RepeatOperationsInterceptor는 메소드 호출을 가로채서 RepeatTemplate에 제공한 CompletionPolicy에 따라 반복 처리한다.

아래 예제는 자바 기반 설정을 사용해서 processMessage 메소드로 서비스를 호출을 반복하게 설정했다 (AOP 인터페이스를 설정에 대한 자세한 정보는 Spring User Guide를 참고하라):

@Bean
public MyService myService() {
	ProxyFactory factory = new ProxyFactory(RepeatOperations.class.getClassLoader());
	factory.setInterfaces(MyService.class);
	factory.setTarget(new MyService());

	MyService service = (MyService) factory.getProxy();
	JdkRegexpMethodPointcut pointcut = new JdkRegexpMethodPointcut();
	pointcut.setPatterns(".*processMessage.*");

	RepeatOperationsInterceptor interceptor = new RepeatOperationsInterceptor();

	((Advised) service).addAdvisor(new DefaultPointcutAdvisor(pointcut, interceptor));

	return service;
}

위에 있는 인터셉터는 내부에서 디폴트 RepeatTemplate을 사용한다. policy, listener나 다른 것들을 바꾸고 싶으면 RepeatTemplate 인스턴스를 인터셉터에 주입하면 된다.

가로챈 메소드가 void를 반환하면 인터셉터는 항상 RepeatStatus.CONTINUABLE을 리턴한다 (이런 경우 CompletionPolicy가 종료 시점을 알려주지 않으면 무한 루프에 빠질 위험이 있다). 그 외에는 가로챈 메소드가 null을 반환할 때까지 RepeatStatus.CONTINUABLE를 리턴한다. null을 반환하면 RepeatStatus.FINISHED를 리턴한다. 결과적으로 타겟 메소드 안에 있는 비지니스 로직이 null을 리턴하거나, 주입받은 RepeatTemplateExceptionHandler가 다시 던진 exception을 던져서 더 이상 할 일이 없음을 알릴 수 있다.


전체 목차는 여기에 있습니다.

<< >>