[ksug] spring batch에 대하여 몇가지 질문 드립니다.

455 views
Skip to first unread message

Myoungsoo Shin

unread,
Dec 28, 2009, 2:57:57 AM12/28/09
to ks...@googlegroups.com
안녕하세요 스프링 배치를 오늘 부터 공부하게 된 스프링 초보입니다. 
몇가지 궁금한 점이 있어서 염치불구하고 질문을 드리고 싶습니다. :-)

1. 실패한 Item들만 다시 배치 작업이 가능할까요?
실패한 JobInstance에 대하여 실패한 시점부터는 가능한것으로 알고 있는데요. 
실패한 Items만 따로 다시 특정시간(예:1시간후,2시간후) retry-limit을 줄수 있을까요?

2. 1:다로 된 구조(부모-자식관계)에서 각각 ItemReader을 만들고 자식 ItemReader에서 부모 ItemReader의 정보(예:metaId)을 읽어서 read을 할수 있을까요?
디비가 아니라 xml이 그런식의 구조인데요. 흠. 가능 할까요? +_+
제가 또 우문을 던지는 것 같긴 한데요.  한 job이나 step에서 ItemReaders나 ItemWriters끼리 데이터를 공유할수 있는 방법이 있을까요? +_+


3. 특정 rest based api에서 리턴하는 XML을 ItemReader로 읽고 싶은데요. ItemReader의 Resource을 UrlResource을 이용해서 StaxEventItemReader을 사용하면 될까요?
첨 생각은 Spring 3.0에 나온 RestTemplate을 사용해서 읽으면 어떨까 싶었는데요. ItemProcessor으로 처리하는게 맞는지도 궁금합니다. 

스프링 배치 넘 어렵네요. T_T
읽어주셔서 감사합니다.

--
email: anar...@gmail.com
blog: http://anarcher.appspot.com/

Sanghyuk Jung

unread,
Dec 28, 2009, 4:16:52 AM12/28/09
to ks...@googlegroups.com
1. 실패한 건을 따로 Listener를 이용해서 특정 폴더에 쓴다던지 해서 모아두고, 그것을 처리하는 job을 따로 실행시키는 것이 나을듯합니다.ItemWriteListener.onWriteError 같은 것을 이용해서 실패한 건만 따로 이벤트 처리를 할 수 있습니다. 그 안에서 별도의 디렉토리에 쓴다던지DB에 표시한다던지해서(같이 롤백되지 않게 트랜잭션 처리 주의해야 합니다.) 한두시간 후에 별도의 스케뷸로 실패로 분류된 것만 다시 실행시킬 수 있습니다.
 
2. 여러가지 방법을 생각해볼수 있습니다.  ItemProcessor를 이용해서 여러개의 ItemReader를 결합해서 부모,자식간의 처리를 해 줄수도 있습니다.  DB의 부모-자식관계가 있는 엔티티를 XML로 옮기는 경우에 N+1쿼리 문제가 발생하지 않도록 2개의 JdbcCursorItemReader를 이용해서 키순대로 정렬해서 DB에서 조회해준다음에 ItemProcessor안에서 엮어서 XMl을 만드는 것도 해본적이 있습니다. (이 작업은 1.x대에서 해서 그때는 DelegateItemReader인가.. 그걸  썼었지요;)
그리고 보통은 ExcecutionContext를 이용해서 같은 step이나 job안에서 데이터를 공유하고, ItemListener와 ItemProcessor 등의 인터페이스를 동시에 구현하는 클래스를 만들어서 같은 클래스의 멤버변수로 데이터를 공유할 수도 있습니다. (이 경우에는 객체가 계속 쌓이지 않게 메모리 릭에 주의해야겠죠)
 
3. UrlResource를 바로 이용해서 연결해도 됩니다. 그런데 아무래도 나중에 실패시 추적등을 위해서 URL에서 파일로 download 받아 놓는 것이 좋은 것 같아서 저는 비슷한 경우에 다운로드를 하는 step를 하나 추가하고, 다운된 파일을 읽어서 xml파싱을 했었습니다. 아래와 같은 소스를 이용했었습니다.

public class ResourceCopyTasklet implements Tasklet {
 
 private Resource source;
 private Resource destination;
 public void setSource(Resource source) {
  this.source = source;
 }
 public void setDestination(Resource destination) {
  this.destination = destination;
 }
 @Override
 public RepeatStatus execute(StepContribution contribution,
   ChunkContext chunkContext) throws Exception {
  Assert.notNull(source,"source must not null");
  Assert.notNull(destination,"destination must not null");
  
  FileUtils.copyURLToFile(source.getURL(), destination.getFile());
  contribution.incrementReadCount();
  contribution.incrementWriteCount(1);
  return RepeatStatus.FINISHED;
 }
}
그리고 설정할때는 아래처럼하죠
 <bean id="resouceCopyTasklet" class=".....ResourceCopyTasklet">
  <property name="source" value="http://www.naver.com"/>
  <property name="destination" value="file:target/output.txt"/>
 </bean>
 
아래와 같은 식으로 하면 다운로드한 파일에 jobParameter의 일부를 넣는 것도 가능합니다.  (이때는 scope="step"으로 해야합니다.)
 
  <property name="destination"
   value="...download_#{jobParameters[downloadDate]}.xml" />
이렇게 각 시도때마다 일단 파일을 다운받아 놓으니 나중에 에러가 났을 때 추적하기도 괜찮더군요.
 
 
 
2009년 12월 28일 오후 4:57, Myoungsoo Shin <anar...@gmail.com>님의 말:

--

Google 그룹스 'Korea Spring User Group' 그룹에 가입했으므로 본 메일이 전송되었습니다.
이 그룹에 게시하려면 ks...@googlegroups.com(으)로 이메일을 보내세요.
그룹에서 탈퇴하려면 ksug+uns...@googlegroups.com로 이메일을 보내주세요.
더 많은 옵션을 보려면 http://groups.google.com/group/ksug?hl=ko에서 그룹을 방문하세요.

Myoungsoo Shin

unread,
Dec 28, 2009, 4:29:05 AM12/28/09
to ks...@googlegroups.com
와. 정말 감사합니다. +_+ 
좀더 자세히 읽어보고 생각해보겠습니다. 
친절한 답변 다시 한번 감사드립니다.

ps) SpringBatch 책 하나 쓰실 생각 없으신가요? +_+


2009/12/28 Sanghyuk Jung <ben...@gmail.com>

Sanghyuk Jung

unread,
Dec 28, 2009, 5:18:44 AM12/28/09
to ks...@googlegroups.com
네, 책은 쓰고는 있는데, 언제 나올지는 모르겠네요 ^^;

 
2009년 12월 28일 오후 6:29, Myoungsoo Shin <anar...@gmail.com>님의 말:

신승한

unread,
Dec 28, 2009, 6:06:20 AM12/28/09
to ks...@googlegroups.com

책 무진장 기대하고 있습니다~



2009년 12월 28일 오후 7:18, Sanghyuk Jung <ben...@gmail.com>님의 말:

Myoungsoo Shin

unread,
Jan 4, 2010, 3:32:49 AM1/4/10
to ks...@googlegroups.com
이번에 처음으로 스프링 배치를 써봤습니다. (아주 간단한 것이었지만요 :-) 

2009/12/28 Sanghyuk Jung <ben...@gmail.com>

1. 실패한 건을 따로 Listener를 이용해서 특정 폴더에 쓴다던지 해서 모아두고, 그것을 처리하는 job을 따로 실행시키는 것이 나을듯합니다.ItemWriteListener.onWriteError 같은 것을 이용해서 실패한 건만 따로 이벤트 처리를 할 수 있습니다. 그 안에서 별도의 디렉토리에 쓴다던지DB에 표시한다던지해서(같이 롤백되지 않게 트랜잭션 처리 주의해야 합니다.) 한두시간 후에 별도의 스케뷸로 실패로 분류된 것만 다시 실행시킬 수 있습니다.
 
RestAPI으로 읽어와서 등록해야 하는 경우이라서 Client Exception(404,500 error등)을 skip하고 SkipListener을 구현해서 따로 디비에 추가했습니다. 
 
2. 여러가지 방법을 생각해볼수 있습니다.  ItemProcessor를 이용해서 여러개의 ItemReader를 결합해서 부모,자식간의 처리를 해 줄수도 있습니다.  DB의 부모-자식관계가 있는 엔티티를 XML로 옮기는 경우에 N+1쿼리 문제가 발생하지 않도록 2개의 JdbcCursorItemReader를 이용해서 키순대로 정렬해서 DB에서 조회해준다음에 ItemProcessor안에서 엮어서 XMl을 만드는 것도 해본적이 있습니다. (이 작업은 1.x대에서 해서 그때는 DelegateItemReader인가.. 그걸  썼었지요;)
그리고 보통은 ExcecutionContext를 이용해서 같은 step이나 job안에서 데이터를 공유하고, ItemListener와 ItemProcessor 등의 인터페이스를 동시에 구현하는 클래스를 만들어서 같은 클래스의 멤버변수로 데이터를 공유할 수도 있습니다. (이 경우에는 객체가 계속 쌓이지 않게 메모리 릭에 주의해야겠죠)
 
여러 API을 호출하여 여러 테이블에 등록해야 하는 경우이라서 ItemProcessor에서 여러번 호출하여 모델들에게 추가하는 것으로 했습니다. CompositeReader나 CompositeWriter등이 있어서 재사용이 생긴다면 각각 ItemProcessor나 ItemReader을 만들고 
조합해도 될듯 한데요. (귀찮아서 안 그랬어염.;;)
ItemStream을 구현해서 상태를 가지고 처리하는 것도 나쁘지 않을 듯 합니다. 흠. 
역시 저는 디비에다가 추가했습니다. ㅎㅎ 좋은 방법은 아니지만 ItemProcessor에 Service을 DI해서 그냥 추가했습니다.;;

며칠 안 써봤지만. 첨으로 써봤다는 것에 의미를 부여하고 있습니다. ㅎㅎ
친절한 답변 다시 한번 감사드립니다. :-) 

Reply all
Reply to author
Forward
0 new messages