2009/01/02 11:53

Spring Batch 1.0에서 2.0으로 진화하기- 1. ItemReader/ItemWriter(2)

ItemWriter는 처리 흐름을 재설계하면서, API 개선과 구현 효율성을 이끌어온 경우라 볼 수 있습니다.

기존 ItemWriter의 처리 흐름을 살펴보면 아래와 같습니다.

사용자 삽입 이미지

read() - wirte()의 호출이 1 : 1 였습니다. 한 번 읽고, 한 번 쓰기죠. 하지만 이런 처리 흐름이 대용량 데이터 처리 시에는 지나치게 작은 쓰기 작업의 처리 단위가 됐기 때문에, 이를 보완하기 위해서 flush라는 개념을 사용했습니다. 그래서 write()에서는 실제 쓰기 작업을 수행하는게 아니라, 단지 버퍼에 해당 아이템을 기록해 두었고, 실제로 쓰기 작업은 flush가 발생했을 때, 즉 flush() 메소드가 호출되는 시점에 하도록 설계가 됐습니다.

하지만 2.x에서는 이 처리 흐름이 1 : 1에서 모두 : 1 로 변경 됐습니다.
사용자 삽입 이미지
즉, read()로 한 번에 모든 아이템을 불러 온 다음, 쓰기 작업 해야 하는 아이템의 목록을 한 번에 받아서 wirte() 내에서 처리하도록 변경 됐습니다. (read()에서 한 번에 모든 데이터를 받아오는 것이 좋지 않지만, 이는 한 번에 처리하는 chunk의 아이템 개수를 제한하는 방법과 함께 사용하면 됩니다.)

# 1.x ItemWrite
public interface ItemWriter {
   void write(Object item) throws Exception;
   void flush() throws FlushFailedException;
   void clear() throws ClearFailedException;
}

# 2.x ItemWrite
public interface ItemWriter<T> {
   void write(List<? extends T> items) throws Exception;
}

그러므로 쓰기 작업 대상이 되는 아이템을 버퍼에 저장해뒀다 한 번에 써버리는 일은 이제 필요 없게 됐습니다.

FlatFileItemWriter 구현을 보면 1.x에서는 write()가 단지 버퍼에 아이템을 추가하고, flush()에서 실제 쓰기 작업을 하던 것을, 2.x에서는 write() 메소드 내이 실제 쓰기 작업의 구현이 되어 있는 것을 볼 수 있습니다.

# 1.x
public void write(Object data) throws Exception {
   if(getOutputState().isInitialized()){
       FieldSet fieldSet = fieldSetCreator.mapItem(data);
       lineBuffer.add(lineAggregator.aggregate(fieldSet) + lineSeparator);
   }
   else{
       throw new WriterNotOpenException("Writer must be open before it can be written to");
   }
}

public void flush() throws FlushFailedException {
   OutputState state = getOutputState();
   for (Iterator iterator = lineBuffer.listIterator(); iterator.hasNext();) {
       String line = (String) iterator.next();
       try {
           state.write(line);
       }
       catch (IOException e) {
           throw new FlushFailedException("Failed to write line to output file: " + line, e);
       }
   }
   lineBuffer.clear();
   state.mark();
}


# 2.x
public void write(List<? extends T> items) throws Exception {

    if (!getOutputState().isInitialized()) {
       throw new WriterNotOpenException("Writer must be open before it can be written to");
   }

    OutputState state = getOutputState();

    StringBuilder lines = new StringBuilder();
   int lineCount = 0;
   for (T item : items) {
       lines.append(lineAggregator.aggregate(item) + lineSeparator);
       lineCount++;
   }
   try {
       state.write(lines.toString());
   }
   catch (IOException e) {
       throw new FlushFailedException("Could not write data.  The file may be corrupt.", e);
   }
   state.linesWritten += lineCount;
}

1.x의 write()+flush() 구현 내용이 2.x에서는 write()로 통합된 것이죠.

1.x와 2.x의 ItemWriter를 비교해보면 상당 부분 많은 클래스들이 변경 된것을 알 수 있습니다.

# 1.x ItemWriter hierarchy
사용자 삽입 이미지

# 2.x ItemWriter hierarchy
사용자 삽입 이미지

물리적인 리소스 파일을 갖게 되는 두 개의 구현클래스가 Resource를 직접 제어할 수 있도록 리소스 파일을 참조할 수 있도록 해주는 인터페이스도 도입이 됐네요..

한 가지 궁금했던 점은 1.x에 있던 HibernateAwareItemWriter 클래스 였는데, 2.x에서는 아예 사라져버렸습니다. 이제 처리할 아이템의 목록이 한 번에 넘어오니 굳이 기본 구현체를 제공할 필요가 없다고 생각했나 봅니다^^;
Trackback 0 Comment 0