티스토리 툴바



2008/09/10 00:54

[Spring Batch 쓰임새 분석] 단순한 배치 반복하기


원문: Use Case: Simple Batch Repeat

목적

데이터 아이템이나 메시지 처리와 같은 단순한 연산 반복으로, 정해진 시간까지 반복하며, 보통 전체 배치 범위를 트랜잭션으로 처리한다. 트랜잭션 자원은 배치 연산들 사이에 공유되며, 이는 성능 향상을 불러온다.


범위

  • 자체적인 I/O나 데이터베이스 자원을 사용하고, 관리하도록 기대할 수도 있지만, 트랜잭션이 반드시 필요한건 아니다.
  • 배치 상태를 인트로스펙션(introspection)할 필요가 있을수도 있다.
  • 동기 또는 비동기로 실행
  • 무상태다.
    • 원칙적으로는 프레임웍이 제한을 하지 않지만, 여기서는 구현을 단순화 했다. 이번 절 아래 나오는 구현 예제는 상태유지 동기화라는 점을 기억하자.
  • 필요하다면 POJO로 구현

전제 조건

클라이언트 코드는 배치 연산에서 필요로한 모든 자원을 찾아서, 획득해 놓아야 하며, 테스트가 목적이라면 트랜잭션을 강제로 롤백할 수도 있다.


성공

  • 정해진 시간 안에 성공적으로 배치가 실행됐음을 확인
  • 기반을 두고 있는 트랜잭션이 타임 아웃이 걸리면, 성공적이지만(성공한 트랜잭션은 커밋된다), 일찍이 배치가 완료되었는지 확인
  • 연산 중 일부가 실패해서 배치가 강제 종료되서, 선행돼 실행됐던 연산이 롤백되었는지 확인
  • 비동기로 배치를 실행해서, 정확한 수만큼 연산이 수행되었는지 확인

설명

우리는 종종 배치 연산의 쓰임새 중 다음과 같은 특정한 시나리오에 관심을 갖는다.

  • JMS Destination처럼 종료 지점에서 메세지나 데이터 아이템 읽기
  • 데이터베이스 읽기와 쓰기를 포함하는 비지니스 처리 과정 수행

평범하게 성공적인 배치 쓰임새는 다음처럼 진행된다.

  1. 프레임웍이 배치를 시작하고, 필요한 자원을 얻은 다음, 이번 실행에 대한 context를 생성한다.
  2. 클라이언트는 데이터 아이템 소스와 데이터 아이템에서 실행되는 프로세서의 형식에 맞는 배치 연산을 제공한다.
  3. 프레임웍은 배치 연산을 실행한다.
  4. 배치 크기가 정해진 제한에 다다를 때까지 step을 반복 실행
  5. 프레임웍은 배치를 커밋. 모든 데이터베이스 변경은 커밋되고, 종료 지점에서 삭제된 메세지를 받게 됨

변형

롤백

연산 중 하나가 롤백되면, 예외를 던지게 된다. 일반적인 트랜잭션 의미로 다음에 무슨 일이 일어날지를 결정한다. 대게(위에 설명한 시나리오에서) 배치 전체적으로 외부 트랜잭션이 있게 되는데, 이 역시 함께 롤백된다. 모든 메세지가 보내지 않은상태로 남고, 모든 데이터들은 커밋되지 않은 상태로 남게 된다. 재시도(retry) 시에 정확하게 최초의 상태를 그대로 다시받게 된다.

타임아웃

배치 크기는 고정하지 않는다. 이 쓰임새는 위 시나리오처럼 진행되지만, 배치 연산 실행 도중에 발생한다.

  1. 프레임웍은 배치에서 특정 연산이 타임 아웃이 됐는지를 확인한다.(예를 들어, 메세지를 기다리는 동안에..)
  2. 프레임웍은 지금까지 완료된 배치의 모든 연산을 커밋한다. 커밋되는 연산은 가능한 작게 하자.

비동기 처리

프레임웍이 매번 연산이 완료되기를 기다리는 대신 별도의 쓰레드나 작업 큐를 통해서 연산을 독립적으로 분리해 실행할 수있다. 여전히 배치는 명확하게 지정된 종료지점이 있어야 하기 때문에, 프레임웍은 배치를 완료하기 전에 모든 연산이 끝났는지,실패했는지를 판단하기 위해서 기다린다.

배치 context의 인트로스텍션(introspection)

클라이언트는 실행중인 배치 연산의 상태를 점검해서, 가능한 일찍이 강제로 완료하기를 바랄 수도 있다.


구현

  • 배치 루프의 완료는 루프의 개념적인 전략으로 사용할 수 있는 정책에 위임해서 제어된다. 이는 타임아웃 변형과 일반적인 쓰임새의 흐름을 모두 다룰수 있다.
  • 배치 템플릿(RepeatOperations) 인터페이스는 어떤 형태일까?
batchTemplate.iterate(new RepeatCallback() {

public boolean doInIteration() {
// 여기서 할 일을 하면 됩니다...
}

});

콜백이란 배치 연산을 위한 좋은 툴로써 아이템을 처리하는 핸들러와 함께 데이터 집합이나 메세지 종료지점(ItemProvider)를 통해서 반복을 할 수 있게 해준다. 여기서는 ItemProvider를 알고 있고, 익명 내부클래스처럼 처리를 담당하는 객체를 추가한 RepeatCallback의 구현을 추가했다.

final ItemProvider provider = new JmsItemProvider();
final ItemProcessor processor = new ItemProcessor() {
public void process(Object data) {
// 데이터(기록(record))을 사용해서 할 일 하기...
}
};

batchTemplate.execute(new RepeatCallback() {

public boolean doInIteration() {
Object data = provider.next();
if (data!=null) {
processor.process(data);
}
return data!=null;
}

});

콜백을 사용한 배치 템플릿이 최선의 구현일까? 혹시 TaskExecutor을 사용하거나 재사용할 수 있지 않을까? 개선된 클라이언트를 보도록 하자.

batchTemplate.iterate(new RepeatCallback() {

public boolean doInIteration() {
// 또 할 일을 하면 돼...
}

});

배치 템플릿은 자체적으로 TaskExecutor를 내부적으로 사용하거나,

batchTemplate.iterate(new Runnable() {

public void run() {
// 데이터를 사용해서 할 일을 하자...
};

});

배치 템플릿이 TaskExecutor가 될 수도 있다. 좀더 캡슐화를 할 수 있기 때문에, (아마도) 전자를 더 선호할것이다. 첫 번째 방법이 좀더 복잡한 쓰임새를 수용할 수 있기 때문에, 어쨌든 프레임웍에게 원하는 어떤 방법으로든 템플릿을구현할 수 있는 자유를 더 줄 수 있다.

  • 배치가 끝날때까지 SQL 연산을 비축해서, JDBC드라이버의 효율성을 얻으려면, 클라이언트는 배치 동안에 일부 상태를 저장하고, 트랜잭션 동기화를 할 필요가 있다. 이러한시나리오를 위해 스프링 배치는 템플릿 실행에서 적용되는 인터셉터 프레임웍을 소개한다. 템플릿은 인터셉터를 호출해서, 그 자체로클린업 타입(clean up-type)과 클로즈-타입(close-type) 행동의 전략이 될 수 있다.
public class RepeatTemplate implements RepeatOperations {

public void iterate(RepeatCallback callback) {

// 배치 환경 설정
interceptors.open();

while (running) {

// 인터셉터가 전-처리과정을 거쳐가고, 연산의 지속을 막을 수도..
interceptor.before();

// 배치가 계속 되는 경우에만 계속 호출
if (running = callback.doInIteration()!=null) {
interceptor.after();
}
}

// 전체 배치를 지우거나 커밋
interceptor.close();
}
}

RepeatInterceptor는 상태유지를 할 수 있고, 배치가 끝날때까지 삽입(insert)을 모아 둔다.RepeatTemplate.iterate()이 트랜잭션 처리를 하는 경우, 트랜잭션이 성공한 경우에만 저장해둔 삽입이 일너나게된다.

이 방법은 배치 인터셉터를 사용해서 배치 종료시에 자체적인 트랜잭션을 실행할 것인지를 결정할 수 있게 해준다.

  • 내부 연산이 동기화 돼 트랜잭션 정의를 통해자체적인 타입아웃 메타데이터를 갖기 때문에, 전체적인 배치 타임아웃은 필요 없다. 전체 배치(외부 트랜잭션)는 여전히 타임아웃속성을 갖을 수 있지만, 전체 연산이 한 번에 롤백되려면 시간이 많이 걸리게 된다.
  • 실행중인 배치의 context는 완료정책(completion policy)과 밀접하게 연계되어 있다. 완료 정책은 배치 템플릿에 선택적으로 꽂아 넣을 수있고(pluggable), context 객체의 팩토리로 활동하게 된다. 그러므로 완료 정책은 콜백에서 클라이언트가 검증될 수있다.
public class RepeatTemplate implements RepeatOperations {

public void iterate(RepeatCallback callback) {

// 배치 세션 준비
RepeatContext context = completionPolicy.start();

while (!completionPolicy.isComplete(context)) {

// 콜백은 인자로 context를 받는다
callback.doInIteration(context);

completionPolicy.update(context);
}

}
}

the template is then responsible for registering the current contextwith a RepeatSynchronizationManager. E.g.client code can look at thesession and mark it as complete if desired (c.f. TransactionStatus):

  • 위 예제는 콜백 인터페이스를 통해서 클라이언트가context를 검증할 수 있는 기회를 주고 있다. 클라이언트가 POJO고, 현재 context나 session를 위한 글로벌접근방법(global accessor)이 필요한 경우, 프레임웍은 콜백을 생성해서, 클라이언트를 감싸게 된다. 이때 콜백은RepeatSynchronizationManager를 사용해서 현재 context를 등록할 책임을 지게 된다. 예를 들면,클라이언트 코드가 세션을 살펴보거나 필요하다면 완료로 표시를 하고 싶 수 있는 경우를 들 수 있다. (예를 들면,TransactionStatus)
public Object doMyBatch() {

// 필요한 처리 작업을 수행...

// 원치 않았던 잘못된 일이 발생할 수도 있다...
RepeatContext context = RepeatSynchronizationManager.getContext();
context.setCompleteOnly();

}
크리에이티브 커먼즈 라이선스
Creative Commons License
Trackback 0 Comment 0