본문 바로가기
🛠 BackEnd/Spring

[ Spring ] Spring Batch 이해와 도메인 용어

by 깸뽀 2022. 12. 3.
728x90

📌 배치(Batch)

  • 프로그램의 흐름에 따라 순차적으로 자료를 처리한다는 뜻
  • 배치 처리 = 일괄 처리
  • 배치작업: 사용자가 개입하지 않는 환경에서 특정 완료 시점까지 실행되는것 ex) 은행 점검시간, 월말정산 처리 등
  • 배치애플리케이션을 구현: 하나의 애플리케이션에서 수행하면 성능 저하를 유발할 수 있음

 

Spring Batch 🍃

💡 엔터프라이즈 시스템의 운영에 있어 대용량 일괄처리의 편의를 위해 설계된 가볍고 포괄적인 배치 프레임워크

  • Spring의 틀성을 그대로 가져왔기 때문에 DI, AOP, 서비스 추상화 등 Spring프레임워크의 3대요소를 모두 사용 가능
  • 로깅/투적, 트랜잭션 관리, 작업 처리 통계, 작업 재시작, 건너뛰기, 리소스 관리 등 대용량 레코드 처리에 필수적인 재사용 가능한 기능을 제공
  • 또한 최적화 및 파티셔닝 기술을 통해 대용량 및 고성능 배치 작업을 가능하게 하는 고급 기술 서비스 및 기능을 재공

 

📌 배치 애플리케이션이 필요한 상황

  • 특정한 시점에 스케쥴러를 통해 자동화된 작업이 필요한 경우
  • 실시간 처리가 어려운 대량의 데이터를 처리해야 하는 경우
  • 대용량 데이터의 포맷을 변경, 유효성 검사 등의 작업을 트랜잭션 안에서 처리 후 기록해야 하는 경우

 

📌 배치 어플리케이션의 조건

  • 대용량 데이터: 대량의 데이터를 가져오거나, 전달하거나, 계산하는 등의 처리를 할 수 있어야 함
  • 자동화: 잘못된 데이터를 충돌/중단 없이 처리할 수 있어야 함
  • 견고성: 무엇이 잘못되었는지를 추적할 수 있어야 함 (로깅, 알림)
  • 성능: 지정한 식간 안에 처리 완료하거나 동시에 실행되는 다른 애플리케이션을 방해하지 않도록 수행되어야 함

 

🤚 Spring Batch vs Quartz Scheduler
쿼츠 스케줄러란 단순히 잡을 특정 시간만다 실행이 되도록 도와주는 프로그램으로, Batch와 같이 대용량 데이터 배치 처리에 대한 기능을 지원하지 않는다. 반대로 Batch역시 스케줄 기능을 지원하지 않는다
그렇기 때문에 보통은 Quartz Scheduler + Batch를 같이 사용한다.

 

 


 

📌 스프링 배치 아키텍처(Architecture)

🔹 Application

- Spring Batch를 사용하여 개발자가 작성한 모든 배치 작업 및 사용자 지정코드가 포함되어 있음

- Core, Infrastructure를 이용해 배치의 기능을 만듬

 

 

🔹 Batch Core

- 배치작업을 시작하고 제어하는데 필요한 핵심 런타임 클래스가 포함되어 있음 

- Job, Step, JobLauncher

 

🔹 Batch Infrastructure

- Application, Batch Core 모두 Infrastructure에서 빌드 됨

- ItemReader, ItemWriter, RetryTemplate

 

▶ 개발자는 Application 계층의 비즈니스 로직에 집중 할 수 있고, 배치 동작과 관력된 것은 Batch Core에 있는 클래스들을 이용하여 시작 및 제어할 수 있음

 

 

 

📌 스프링 배치 구조

🔹 Run tier

  • Application의 Scheduling, 실행을 담당.
  • 스프링배치는 따로 Scheduling의 기능을 제공하지 않기 때문에  Quartz나 Cron을 이용해야 함

🔹 Job tier

  • 전체적인 Job의 수행을 책임짐
  • Job 내의 각 Step을 정책에 따라 순차적으로 수행

🔹 Application tier

  • Job을 수행하는데 필요한 Component

🔹 Data tier

  • Database, File 등 물리적 데이터 소스

 

 

📌 스프링 도메인(domain) 용어

https://docs.spring.io/spring-batch/docs/current/reference/html/index-single.html

 

🔹 JobRepository 

- 수행되는 Job에 대한 정보를 담고 있는 저장소

- 실행된 Step, 현재 상태, 읽은 아이템 및 처리된 아이템 수 등이 모두 JobRepository에 저장됨 (=메타데이터)

중요한 부분이니 다시 한번 다뤄보도록 하겠습니다!

 

 

🔹 JobLuncher

- Job을 실행하기 위한 인터페이스 ▶ job.execute를 호출

- Job의 재실행 가능 여부 검증, 잡의 실행 방법, 파라미터 유효성 검증 등을 수행

- Job을 실행하면 해당 Job은 각 Step을 실행하고, 각 Step이 실행되면 JobRepository는 현재 상태로 갱신됨

- Spring Boot환경에서는 Boot가 Job을 시작하는 기능을 제공 일반적으로 직접 다룰 필요가 없는 컴포넌트

- run() 메서드 하나만을 가짐

JobExecution run(Job job, JobParameters jobParameters);

 

🔹 Job

- 배치처리 과정을 하나의 단위로 만들어 표현한 객체로, 여러 Step 인스턴스를 포함하는 컨테이너

- 하나 이상의 Step을 가지고 있으며, JobLauncher에 의해 실행됨

- Job은 N개의 step을 실행할 수 있으며, 흐름(Flow)을 관리할 수 있음

   ex) A Step 실행 후 조건에 따라 B Step 또는 C Step을 실행을 설정

- Job이 실행될 때 스프링 배치의 많은 컴포넌트는 탄력성(resiliency)을 제공하기 위해 서로 상호작용을 함

Job 객체를 만드는 빌더는 여러 개 있기때문에, 여러 빌더를 통합 처리하는 공장인 JobBuilderFactory로 원하는 Job을 쉽게 만들 수 있음

- 기본 구현체: SimpleJob

 

 

 JobBuilderFactory 클래스

- get("job이름") 메서드: JobBuilder를 생성할 수 있음

 jobBuilderFactory.get("inactiveUserJob")

 

 JobBuilder의 메서드

- start(Step step): Step을 추가해서  가장 기본이 되는 SimpleJobBuilder를 생성

- start(Flow flow): Flow를 추가해서 실행할 JobFolwBuilder를 생성

- flow(Step step): Step을 실행할 JobFlowBuilder를 생성

- 모든 반환타입: builder

 

 

🔹 JobInstance

- Job이 실행될 때 하나의 Job 실행단위

- JobInstance = Job + 식별 JobParameters

- Load할 때 데이터와 전혀 관련 없음 (ItemReader가 결정)

- JobExecution들의 모음

- 하루에 한 번씩 배치의 Job이 실행된다면, 각각의 Job을 JobInstance라고 부를 수 있음

🤚

만약 JobInstance가 Job 실행이 실패하면 JobInstance가 끝난것이 아니다.

이 경우 JobExecution이 실패정보를 가지고 있고, 다음날 동일한 JobInstance를 가지고 다시 실행하게 된다.

성공하면 JobInstance는 끝난 것으로 간주성공하면 JobInstance는 끝난 것으로 간주한다.

그러면 이 하나의 JobInstance는 어제 실패한 JobExecution과 오늘 성공한 JobExecution 두개를 가지게 된다.

JobInstanceJobExecution는 부모와 자식관계로, 하나의 JobInstance여러개의 JobExecution을 가지게 된다.

 

 

🔹 JobParameters

- Job이 실행될 때 필요한 파라미터들을 Map타입으로 저장하는 객체

- Batch Job이 실행되는 동안 Job을 식별하거나 Job에서 참조하는 데이터로 사용

- JobInstance를 구분하는 기준이 되기도 함 

- 파라미터 타입: String, Long, Double, Date

🤚

Job 하나를 생성할 때 시작시간 등의 정보를 파라미터로 해서 하나의 JobInstance를 생성한다.

➤  JobInstanceJobParameters는 1:1관계

 

 

🔹 JobExecution

- 단 한 번 시도되는 Job의 실행 시도를 의미

- '시도' :  성공, 진행중, 실패의 상태를 모두 포함

 

🔸Job의 상태

 

✔ BatchStatus: 실행의 상태 

BatchStatus.STARTED 실행 중
BatchStatus.FAILED 실패
BatchStatus.COMPLETED 성공적으로 종료

 

✔ ExitStatus: 실행의 결과

UNKNOWN 알수없음 상태. 더 이상 진행 불가
EXECUTING 실행중(이므로 추가작업이 더 이상 필요없음) 상태
비동기 실행 시 다른 스레드나 프로세스가 처리중이고 결과를 기다릴 필요가 없을 때 사용
COMPLETED 프로세스 종료 상태
NOOP 처리되지 않은 작업을 나타내는 상테 (or 이미 실행이 완료된 상태)
FAILED 에러와 같이 프로세스가 종료된 상태
STOPPED 중단(interrupded)상태로 프로세스가 종료된 상태

 

Job과 관련된 그림

 

🔹 Step

- Job을 구성하는 독립된 작업의 단위 (여러개의 Step이 존재)

- 실제 Batch를 처리하고 정의하는 모든 정보를 포함

- Step에는 Tasklet, Chunk기반 2가지가 있음

- 고유한 JobExecution과 관련된 개별 StepExecution이 존재함

 

Tasklet

- Step이 중지될 때까지 execute 메서드가 계속 반복해서 수행하고 수행할 때마다 독립적인 트랜잭션이 얻어짐

- 초기화, 저장 프로시저 실행, 알림 전송과 같은 잡에서 일반적으로 사용됨

 

Chunk

- 한 번에 하나씩 데이터(row)를 읽어 Chunk라는 덩어리를 만든 뒤, Chunk 단위로 트랜잭션을 다루는 것

- Chunk 단위로 트랜잭션을 수행하기 때문에 실패할 경우엔 해당 Chunk 만큼만 롤백이 되고, 이전에 커밋된 트랜잭션 범위까지는 반영이 됨

- Chunk 기반 Step은 ItemReader, ItemProcessor, ItemWriter라는 3개의 주요 부분으로 구성될 수 있음.

Item은 배치 처리 대상 객체를 의미

ItemReader와 ItemProcessor에서 데이터는 1건씩 다뤄지고, Writer에선 Chunk 단위로 처리된다.

 

 

🔹 StepExecution

- Step의 단일 실행을 시도하는 기술적인 개념

- 각각의 Step마다 새로운 SetpExecution이 생성

- Step이 실패하면 이후 Step은 실행하지 않음

- 각 Step에는 ExecutionContext (통계, 상태정보와 같이 일괄 실행에서 유지해야 하는 모든 데이터)를 포함.

 

 

🔹 ExecutionContext

- 키와 값을 이루는 콜렉션 (ConcurrentHashMap)

- StepExecution이나 JobExecution에서 개발자가 필요한 영속 상태 저장

- 재시작에 용이

- StepExecution이나 JobExecution의 ExecutionContext는 서로 다른 객체

 

 

🔹 ItemReader

- Step의 대상이 되는 데이터를 읽어오는 인터페이스

- File, DB, XML등 여러 타입의 데이터를 읽어올 수 있음

-  배치 처리 대상 객체를 읽어 ItemProcessor 또는 ItemWriter에게 전달함

- ListItemReader<> 객체"를 사용하면 모든 데이터를 한 번에 가져와 메모리에 올려놓고 read() 메서드로 하나씩 배치 처리 작업을 수행할 수 있음

- 메서드: T read()

- ItemReader에서 read() 메서드의 반환 타입을 제네릭으로 구현했기 때문에 직접 타입을 지정할 수 있음

public interface ItemReader<T> {
    T read() throws Exception, UnexpectedInputException, ParseException, NonTransientResourceException;
}

 

🔹 ItemProcessor

- ItemReader로 읽어온 배치 데이터를 변환하는 역할을 수행

- ItemProcessor이 존재하는 이유: 비즈니스 로직을 분리하기 위해서 ⇒ 유지보수성 용이

- 또는 읽어온 배치 데이터와 쓰여질 데이터 타입이 다를 경우 대응하기 위해서

- 메서드: 0 process(I item);

public interface ItemProcessor<I, O> {
    O process(I item) throws Exception;
}

 

🔹 ItemWriter 

-  배치 데이터를 저장 ⇒ DB에 저장

public interface ItemWriter<T> {
    public write<(List<? extends T> items) throws Exception;
}

 

 

 

 

 

📖 참고

 

728x90

댓글