<Spring Batch> 6. Exception에 대한 skip 처리하기
by BFine
가. Batch Fault
a. Exception
- 배치는 보통 대용량 데이터를 처리하는 경우가 많다. 그렇기 떄문에 그에 따라 다양한 예외사항이 발생할수도 있다.
- 즉 Exception 중에서 Job을 중단 해야할 정도의 크리티컬한 경우와 그렇지 않은 경우가 있을 수 있다.
- Spring Batch에서는 이부분에 대한 클래스와 메서드를 지원하고 있고 간단하게 사용할수가 있도록 제공하고 있다.
나. Skip 처리하기
a. faultTolerant
- Step을 만들때 .faultTolerant 메서드를 볼 수가 있다. 이름처럼 Step에 대한 falut를 허용한다라는 의미를 지니고 있다.
- 자세히 살펴보면 다양한 메서드들을 제공하고 있는 것을 볼 수 있다.
b. skip 과 skipLimit
- 먼저 Exception을 임의로 발생하도록 만들어보고 결과를 살펴보자
@Component
public class AItemProcessor implements ItemProcessor<Input,Output> {
@Override
public Output process(Input item) throws Exception {
System.out.println("## A 처리 - "+item);
throw new IllegalArgumentException("임의로 예외발생 시키기");
}
}
- Step에서 예외가 발생하였고 그에 따라서 Job은 종료되고 프로세스도 내려가는 것을 볼수 있다.
- 이제 이 Exception을 skip 하도록 설정해보자. skipLimit는 해당 Exception을 얼마나 봐줄지 지정해주는 부분이다.
@Bean
public Step step(ItemReader<Input> jpaPagingItemReader, ItemWriter<Output> jpaItemWriter, ItemProcessor<Input,Output> itemProcessor){
return stepBuilderFactory.get("Chunk")
.<Input,Output>chunk(10)
.reader(jpaPagingItemReader)
.processor(itemProcessor)
.writer(jpaItemWriter)
.faultTolerant()
.skip(IllegalArgumentException.class).skipLimit(3)
.build();
}
- Exception이 발생해서 Job은 중단되었지만 설정하기전과 다르게 3번까지는 skip 한 것을 볼 수 있다.
c. 여러 Exception skip
- 배치처럼 대용량 데이터를 처리하는 경우 skip 해야할 Exception이 하나가 아닐 수 있다. 여러 개의 Exception을 추가 해보자
@Component
public class OtherItemProcessor implements ItemProcessor<Input,Output> {
@Override
public Output process(Input item) throws Exception {
System.out.println("## Other - "+item);
throw new IllegalAccessException("임의로 예외발생 시키기");
}
}
@Bean
public Step step(ItemReader<Input> jpaPagingItemReader, ItemWriter<Output> jpaItemWriter, ItemProcessor<Input,Output> itemProcessor){
return stepBuilderFactory.get("Chunk")
.<Input,Output>chunk(10)
.reader(jpaPagingItemReader)
.processor(itemProcessor)
.writer(jpaItemWriter)
.faultTolerant()
.skip(IllegalAccessException.class)
.skip(IllegalArgumentException.class)
.skipLimit(2)
.build();
}
- 똑같이 예외를 발생시키는 부분을 추가했고 그리고 단순히 skip에 Exception을 추가했다. 로그를 살펴보자
- 보면 OtherProcesser는 2번 실행되었는데 왜 skip limit가 초과했지? 3번째에 예외발생해야하는 것 아닌가라는 생각이 들수있다.
- FaultTolerantStepBuilder 내부 코드를 살펴보자
- 코드를 보면 HashMap을 이용하여 skip class를 담는것을 볼 수 있고 skipLimit는 int인 것을 알수 있다.
=> 즉 위의 skip은 하나의 그룹으로 묶이고 각각의 발생횟수의 합계가 skipLimit를 넘으면 예외가 발생한다.
@Bean
public Step step(ItemReader<Input> jpaPagingItemReader, ItemWriter<Output> jpaItemWriter, ItemProcessor<Input,Output> itemProcessor){
return stepBuilderFactory.get("Chunk")
.<Input,Output>chunk(10)
.reader(jpaPagingItemReader)
.processor(itemProcessor)
.writer(jpaItemWriter)
.faultTolerant()
.skip(IllegalAccessException.class).skipLimit(5)
.skip(IllegalArgumentException.class).skipLimit(2)
.build();
}
- 혹시나 위와 같이 처리하면 뭔가 될 것 같다(?)라는 생각이 들수도 있지만 안된다는 것을 조심하자(실제 경험에서 나온 것은 비밀..)
d. 개별적으로 Exception skip 하기
- 위와 다르게 각각의 Exception에 대해 skip 처리해야하는 경우도 있다. 그 전에 SkipPolicy를 살펴보자
- SkipPolicy는 인터페이스명 그대로 어떤식으로 skip 처리 할지를 정책을 정하는 부분이다. 이미지에 보이는 것처럼 5개의 구현체들이 있다.
- 아쉽게도 위의 구현체들로는 개별적인 Exception count를 할 수 없기 때문에 따로 만들어야 한다. 클래스를 만들어보자
public class CustomSkipPolicy implements SkipPolicy {
private final Map<Class<? extends Throwable>,Integer> exceptionClassMap = new HashMap<>();
private final Map<Class<? extends Throwable>,Integer> exceptionCountMap = new HashMap<>();
public void setExceptionClassMap(Class<? extends Throwable> clazz, int limit){
exceptionClassMap.put(clazz,limit);
}
@Override
public boolean shouldSkip(Throwable t, int skipCount) throws SkipLimitExceededException {
if(exceptionClassMap.containsKey(t.getClass())){
int limit = exceptionClassMap.get(t.getClass());
int count = exceptionCountMap.getOrDefault(t.getClass(),0);
if(count+1 > limit){
throw new SkipLimitExceededException(limit,t);
}
exceptionCountMap.put(t.getClass(), count+1);
return true;
}
return false;
}
}
- 여기서 주의해야하는 부분은 skipCount는 모든 Exception 발생에 대해서 count하기 때문에 여기서는 큰 의미를 가지지 않는다.
=> 번외로 비동기 처리를 하는 경우에 skipCount는 스레드마다 개별적으로 값을 가진다.
@Bean
public Step step(ItemReader<Input> jpaPagingItemReader, ItemWriter<Output> jpaItemWriter, ItemProcessor<Input,Output> itemProcessor){
return stepBuilderFactory.get("Chunk")
.<Input,Output>chunk(10)
.reader(jpaPagingItemReader)
.processor(itemProcessor)
.writer(jpaItemWriter)
.faultTolerant()
.skipPolicy(skipPolicy())
.noRetry(Exception.class)
.build();
}
public SkipPolicy skipPolicy(){
CustomSkipPolicy skipPolicy = new CustomSkipPolicy();
skipPolicy.setExceptionClassMap(IllegalArgumentException.class,3);
skipPolicy.setExceptionClassMap(IllegalAccessException.class,3);
return skipPolicy;
}
- 테스트 해보면 설정한대로 limit가 발생한 것을 볼 수 있다. 이런식으로 SkipPolicy 같은 경우 custom하게 처리가 가능하다.
'공부 > Spring Batch' 카테고리의 다른 글
<Spring Batch> 5. ItemProcessor, ItemWriter (0) | 2021.10.03 |
---|---|
<Spring Batch> 4. Chunk 기반 배치, ItemReader (0) | 2021.10.02 |
<Spring Batch> 3. 배치 테이블 Embedded DB(H2)로 따로 관리하기 (1) | 2021.09.27 |
<Spring Batch> 2. JobRepository, Config Customizing (0) | 2021.09.22 |
<Spring Batch> 1. JobParameters, ExecutionContext (0) | 2021.09.21 |
블로그의 정보
57개월 BackEnd
BFine