-
Spring BatchSTUDY/백엔드 2021. 3. 3. 01:00
대용량의 데이터를 처리하는 어플리케이션을 배치 어플리케이션이라고 한다.
1. 배치 어플리케이션 조건
- 대용량 데이터 - 배치 어플리케이션은 대량의 데이터를 가져오거나, 전달하거나, 계산하는 등의 처리를 할 수 있어야 한다.
- 자동화 - 배치 어플리케이션은 심각한 문제 해결을 제외하고는 사용자 개입 없이 실행되어야 한다.
- 견고성 - 배치 어플리케이션은 잘못된 데이터를 충돌/중단 없이 처리할 수 있어야 한다.
- 신뢰성 - 배치 어플리케이션은 무엇이 잘못되었는지를 추적할 수 있어야 한다. (로깅, 알림)
- 성능 - 배치 어플리케이션은 지정한 시간 안에 처리를 완료하거나 동시에 실행되는 다른 어플리케이션을 방해하지 않도록 수행되어야한다.
대용량 데이터를 실시간으로 처리하기에는 조회 시간이나 서버 부하가 심해지기 때문에,
정해진 시간에 대용량 데이터를 한 번에 처리해서 요청이 올 경우 미리 처리해둔 데이터를 바로 전달할 수 있도록 한다.
* dependency 추가
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-batch</artifactId>
</dependency>* 스프링 배치 기본 시나리오
Read(가져와서) : 원하는 조건의 데이터 레코드를 DB에서 읽어온다.
Processing(처리하고) : 읽어온 데이터를 비즈니스 로직을 따라 처리한다.(생략 가능, Write에서 바로 처리)
Write(저장한다.) : 처리된 데이터를 DB에 업데이트(저장)한다.Job
한개 혹은 여러 개의 Step을 이루어 하나의 단위로 만들어 표현한 객체이다. Job 객체는 JobBuilderFactory 클래스에서(JobBuilderFactory에서 생성된 JobBuilder를 통해) Job을 생성할 수 있다.
Step
기본구조(읽고, 처리하고, 저장한다.)는 Step이라는 객체에서 정의됩니다. 1개의 Step은 읽고, 처리하고 저장하는 구조를 가지고 있는 가장 실질적인 배치처리를 담당하는 도메인 객체이다. 그리고 이 Step은 한 개 혹은 여러 개가 이루어 Job을 표현한다. 그리고 1개의 Step은 ItemReader, ItemProcessor, ItemWriter를 정의하거나 Tasklet을 정의한다.
ItemReader : 배치데이터를 읽어오는 인터페이스이다. DB뿐 아니라 File, XML 등 다양한 타입에서 읽어올 수 있다.
public interface ItemReader<T> {
T read() throws Exception, UnexpectedInputException, ParseException, NonTransientResourceException;
}
ItemProcessor : 읽어온 데이터를 가공/처리한다. 즉, 비즈니스 로직을 처리한다.(I가 Input, O가 Output type을 의미)
public interface ItemProcessor<I, O> {
O process(I item) throws Exception;
}
ItemWriter : 처리한 데이터를 DB(또는 파일)에 저장한다.
public interface ItemWriter<T> {
void write(List<? extends T> items) throws Exception;
}Chunk
Chunk란 데이터 덩어리로 작업 할 때 각 커밋 사이에 처리되는 row 수를 말한다.
즉, Chunk 지향 처리란 한 번에 하나씩 데이터를 읽어 Chunk라는 덩어리를 만든 뒤, Chunk 단위로 트랜잭션을 다루는 것을 의미한다.
Chunk 단위로 트랜잭션을 수행하기 때문에 실패할 경우엔 해당 Chunk 만큼만 롤백이 되고, 이전에 커밋된 트랜잭션 범위까지는 반영이 된다.Chunk 지향 처리는 결국 Chunk 단위로 데이터를 처리한다는 의미이다.
- Reader에서 데이터를 하나 읽어온다.
- 읽어온 데이터를 Processor에서 가공한다.
- 가공된 데이터들을 별도의 공간에 모은 뒤, Chunk 단위만큼 쌓이게 되면 Writer에 전달하고 Writer는 일괄 저장한다.
Reader와 Processor에서는 1건씩 다뤄지고, Writer에선 Chunk 단위로 처리된다.
* chunk size와 page size의 차이
- chunk size는 한번에 처리될 트랜잭션 단위를 얘기하고, page size는 한번에 조회할 item의 양을 얘기한다. 즉 page size만큼 데이터를 조회해서 하나씩 데이터를 읽어 처리하면서 쌓고, chunk size 크기만큼 쌓이면 writer에 전달해서 일괄 저장하게 된다.
- page size가 10이고, chunk size가 50이면 한번의 트랜잭션을 수행하기 위해 데이터 조회가 chunk size / page size = 5번 이루어진다. 한번의 트랜잭션을 위해 5번의 조회가 이루어지면 성능상 이슈가 발생할 수 있다. 그래서 보통은 page size와 chunk size를 동일하게 설정하거나 page size를 더 크게 설정한다.
Setting a fairly large page size and using a commit interval that matches the page size should provide better performance.
(상당히 큰 페이지 크기를 설정하고 페이지 크기와 일치하는 커미트 간격을 사용하면 성능이 향상됩니다.)* chunk 내부 처리 : github.com/jojoldu/spring-batch-in-action/blob/master/6_CHUNK.md
JobLauncher
구현한 Job을 실행할 수 있는 RunJob이라는 이름의 메소드 한개만 가진 인터페이스이다.
public interface JobLauncher {
JobExecuteState runJob(String jobName, Map<String, String> jobParameters);
}JobRepository
배치 작업 중의 정보를 저장하는 역할을 한다. 어떠한 Job이 언제 수행되었고, 언제 끝났으며, 몇 번이 실행되었고 실행에 대한 결과가 어떤지 등의 배치 작업의 수행과 관련된 모든 meta data가 저장되어 있다. 배치작업이 처음 실행되면 JobRepository에서 JobExecution이 생성되고 배치작업이 실행되는 동안 StepExecution 및 JobExecution의 정보들이 JobRepository에 저장되고 갱신되어 지속된다.
JobInstance, JobExcution
Job이 한 번 실행될 때 JobInstance라는 실행 단위 인스턴스가 만들어진다. JobInstance는 JobParameter 값에 따라 구분되어 생성된다. 이 인스턴스는 여러 번의 실행을 가질 수 있는데, 이 여러 번의 실행 단위를 JobExcution이라고 한다.
배치 실행 정보, 메타 테이블[ ahndy84.tistory.com/24?category=339592 ]
Scope
@JobScope는 Step선언문에서만 사용이 가능합니다.
@StepScope는 Step을 구성하는 ItemReader, ItemWriter, ItemProcessor에서 사용 가능합니다.Spring Bean의 기본 scope는 singleton인데 @JobScope 또는 @StepScope를 사용하게 되면, @JobScope는 Job 실행시점에 bean이 생성되고, @StepScope는 Step 실행시점에 bean으로 생성된다. Bean의 생성시점을 어플리케이션 실행 시점이 아닌, Job 혹은 Step의 실행시점으로 지연시키면서 얻는 장점은 크게 두가지가 있다.
1. JobParameter의 Late Binding이 가능하다.
- JobParameter를 특정메서드가 실행하는 시점까지 지연시켜 할당시킬 수 있습니다. 애플리케이션이 구동되는 시점이 아니라 비즈니스로직이 구현되는 어디든 JobParameter를 할당함으로 유연한 설계를 가능하게 합니다.2. 동일한 컴포넌트를 병렬 혹은 동시에 사용할때 유용하다.
- 하나의 메소드가 서로 다른 Step으로 부터 동시에 병렬실행이 된다면 서로의 상태를 간섭을 받게 될 수 있습니다. 하지만 앞서 @StepScope를 명시해 놓을으로써 각각의 Step에서 실행될 때 서로의 상태를 침범하지 않고 처리를 완료할 수 있습니다.
@EnableBatchProcessing
- @EnnableBatchProcessing 선언을 통하여 배치 애플리케이션을 구동하는데 필요한 설정을 자동으로 등록시켜 준다.
- DefaultBatchConfigurer를 상속받아서 필요한 설정을 등록해서 사용할 수 있다.
@Slf4j
@Configuration
@EnableBatchProcessing
public class SimpleJobConfiguration {
private final JobBuilderFactory jobBuilderFactory;
private final StepBuilderFactory stepBuilderFactory;
@Bean public Job simpleJob() {
return jobBuilderFactory.get("simpleJob")
.start(simpleStep1())
.build();
}
@Bean public Step simpleStep1() {
return stepBuilderFactory.get("simpleStep1")
.tasklet((contribution, chunkContext) -> {
log.info(">>>>> This is Step1");
return RepeatStatus.FINISHED;
})
.build();
}
}- @Configuration
- Spring Batch의 모든 Job은 @Configuration으로 등록해서 사용
- jobBuilderFactory.get("simpleJob")
- simpleJob 이란 이름의 Batch Job을 생성
- job의 이름은 별도로 지정하지 않고, Builder를 통해 지정
- stepBuilderFactory.get("simpleStep1")
- simpleStep1 이란 이름의 Batch Step을 생성
- jobBuilderFactory.get("simpleJob")와 마찬가지로 Builder를 통해 이름을 지정
- .tasklet((contribution, chunkContext))
- Step 안에서 수행될 기능들을 명시
- Tasklet은 Step안에서 단일로 수행될 커스텀한 기능들을 선언할때 사용
- 여기서는 Batch가 수행되면 log.info(">>>>> This is Step1") 가 출력
@Bean public Step simpleStep1() {
return stepBuilderFactory.get("simpleStep1")
.tasklet((contribution, chunkContext) -> {
log.info(">>>>> This is Step1");
return RepeatStatus.FINISHED;
})
.build();
}
---------------------------------------------------------------------------------------------------------------------------
@Bean public Step simpleStep1() {
return stepBuilderFactory.get("simpleStep1")
.tasklet(simpleTasklet)
.build();
}
@Slf4j
@Component
@StepScope
public class SimpleTasklet implements Tasklet {
@Override
public RepeatStatus execute(StepContribution stepContribution, ChunkContext chunkContext) {
log.info(">>>>> This is Step1");
return RepeatStatus.FINISHED;
}
}Spring Batch에서 Job은 하나의 배치 작업 단위를 얘기한다. Job 안에는 여러 Step이 존재하고, Step 안에는 Tasklet 또는 Reader & Processor & Writer 묶음이 존재한다.
@Slf4j
@Configuration
public class StepNextJobConfiguration {
private final JobBuilderFactory jobBuilderFactory;
private final StepBuilderFactory stepBuilderFactory;
@Bean
public Job stepNextJob() {
return jobBuilderFactory.get("stepNextJob")
.start(step1())
.next(step2())
.next(step3())
.build();
}
@Bean
public Step step1() {
...
}
@Bean
public Step step2() {
...
}
@Bean
public Step step3() {
...
}
}- next는 순차적으로 step을 연결해서 실행시킬 때 사용된다. step1 -> step2 -> step3 순서로 실행된다.
- 조건별 흐름 제어(Flow)
- 스프링 배치 Step의 Flow는 각 Step의 Status에 따라 진행된다.
- 기본적으로 Step이 성공하면 다음 Step으로 넘어가고, Step이 실패하면 Job이 실패한다.
- 좀 더 복잡한 시나리오를 생성하면 Step1이 실패하면 Step3으로 넘어가고, 그외에는 Step2로 넘어갔다가 Step3으로 넘어가는 것도 가능하다.
@Slf4j
@Configuration
public class StepNextConditionalJobConfiguration {
private final JobBuilderFactory jobBuilderFactory;
private final StepBuilderFactory stepBuilderFactory;
@Bean
public Job stepNextConditionalJob() {
return jobBuilderFactory.get("stepNextConditionalJob")
.start(conditionalJobStep1())
.on("FAILED") // step1로부터 FAILED 일 경우
.to(conditionalJobStep3()) // step3으로 이동한다.
.on("*") // step3의 결과 관계 없이
.end() // step3으로 이동하면 Flow 종료
.from(conditionalJobStep1()) // step1로부터
.on("*") // FAILED 외에 모든 경우
.to(conditionalJobStep2()) // step2로 이동한다.
.next(conditionalJobStep3()) // step2가 정상 종료되면 step3으로 이동한다.
.on("*") // step3의 결과 관계 없이
.end() // step3으로 이동하면 Flow 종료
.end() // Job 종료
.build();
}
@Bean public Step conditionalJobStep1() {
...
}
@Bean
public Step conditionalJobStep2() {
...
}
@Bean
public Step conditionalJobStep3() {
...
}- .on()
- 캐치할 ExitStatus 지정
- * 일 경우 모든 ExitStatus가 지정된다.
- to()
- 다음으로 이동할 Step 지정
- from()
- 일종의 이벤트 리스너 역할
- 상태값을 보고 일치하는 상태라면 to()에 포함된 step을 호출합니다.
- step1의 이벤트 캐치가 FAILED로 되있는 상태에서 추가로 이벤트 캐치하려면 from을 써야만 함
- end()
- end는 FlowBuilder를 반환하는 end와 FlowBuilder를 종료하는 end 2개가 있다.
- on("*")뒤에 있는 end는 FlowBuilder를 반환하는 end
- build() 앞에 있는 end는 FlowBuilder를 종료하는 end
- FlowBuilder를 반환하는 end 사용시 계속해서 from을 이어갈 수 있다.
여기서 중요한 점은 on이 캐치하는 상태값이 BatchStatus가 아닌 ExitStatus라는 점이다.
Batch Status
- JobExecution과 StepExecution의 속성으로, Job과 Step의 상태를 의미합니다.
- Status 값으로는 OMPLETED, STARTING, STARTED, STOPPING, STOPPED, FAILED, ABANDONED or UNKNOWN 가 있습니다.
Exit Status
- Job과 Step의 Execution이 끝난 후의 상태를 의미하며, 위에서 언급했듯이 Step의 시나리오 흐름에 영향을 미치는 상태 값입니다.
- on 속성에 매칭되는 값.
- 기본적으로 exit status 값은 batch status 값과 항상 동일합니다.
블로그 참고
'STUDY > 백엔드' 카테고리의 다른 글
Optional (0) 2021.05.06 Stream (0) 2021.04.28 메모리 영역 (0) 2021.02.08 RestTemplate & WebClient (0) 2021.02.03 @Mock, @MockBean (0) 2021.02.03