LostCatBox

spring-batch 해보기

Word count: 390Reading time: 2 min
2025/01/26 Share

참조

배달의민족 라빈의 스프링 배치

왜?

사이드 프로젝트에서 spring-batch가 필요했다.

대법원 사이트에 들어가서 크롤링할 것들이 많았고, 이를 사프-DB에 저장해야하기 때문이다.

batch 특성상 대용량 데이터 처리에 적합하기 때문에 적합하다고 판단하였다.

배치 구성 및 요약

img

img

LobLauncher

  • job을 실행시켜주는 역할

JobRepository

  • job, step excution 기록 역할
  • job, step에 대한 성공, 실패, readcount, write count 등의 값을 DB에 기록해주고 있으며, 개발자가 비즈니스 로직에 집중할수있게 도와줌

Job

  • Job
    • 할 일이 정의되면, Step들로 이뤄져있음
  • JobInstance
    • job+ identity값을 parameter로 유일한 특징이 있음.
    • 예를 들면 2025년 1월 26일 00:00:00과 2025년 1월 27일 00:00:00는 다른 jobInstance
  • JobExecution
    • JobInstance 를 실행시켜주는 역할, 상태값을 가짐.
    • DB에 상태값 기록됨

Step

스크린샷 2025-01-26 오후 8.03.26

  • Step
    • 실제 비즈니스 로직을 정의
  • StepExecution
    • Step를 실행시켜주는 역할, 상태값을 가짐.
    • DB에 상태값 기록됨
  • ItemReader
    • Step 부분 중 Item(대상 row 지칭)을 read하는 역할
  • ItemProcessor
    • Step 부분 중 read된 Item을 비즈니스 로직을 처리하는 역할
    • Optional
  • ItemWriter
    • Step 부분 중 상태변화 완료된 Item(대상 row 지칭)을 기록하는 역할

예시

  • “1~100 item 처리완료” 메시지 10개씩 10번 출력되는 배치잡이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

@Configuration
@EnableBatchProcessing
class BatchConfig(
private val jobBuilderFactory: JobBuilderFactory,
private val stepBuilderFactory: StepBuilderFactory
) {

private val log = LoggerFactory.getLogger(BatchConfig::class.java)

private val jobName = "testTaskChunkJob"

@Bean
fun testTaskChunkJob(): Job {
return jobBuilderFactory.get(jobName)
.incrementer(RunIdIncrementer())
.start(chunkStep1())
.build()
}

@Bean
fun chunkStep1(): Step {
return stepBuilderFactory.get("chunkStep1")
.chunk<String, String>(10)
.reader(itemReader())
.processor(itemProcessor())
.writer(itemWriter())
.build()
}

private fun itemReader(): ItemReader<String> {
return ListItemReader(getItems())
}

private fun itemProcessor(): ItemProcessor<String, String> {
return ItemProcessor { item -> "$item 처리완료" }
}

private fun itemWriter(): ItemWriter<String> {
return ItemWriter { items -> log.info("### writer : ${items.toString()}") }
}

private fun getItems(): List<String> {
val items = mutableListOf<String>()
for (i in 1..100) {
items.add("item$i")
}
return items
}
}

추가적인 용어정리 및 팁

용어정리

  • 청크(chunk): 각 커밋 사이에 처리될 row(item)의 수
    • 성공시 chunk만큼 커밋, 실패시 chunk 만큼 롤백
  • 페이징(paging): itemReader에서 제공하는 기능. read하는 item들의 갯수

  • 청크와 페이징 수는 반드시 동일해야한다.
    • 성능이슈(JPA기준으로 청크 > 페이징일 경우 step이 끝나고 commit 일어나지않고, 다음 작업을 위해 reading하면 메모리에 2배의양이..로드됨)
CATALOG
  1. 1. 참조
  2. 2. 왜?
  3. 3. 배치 구성 및 요약
    1. 3.1. LobLauncher
    2. 3.2. JobRepository
    3. 3.3. Job
    4. 3.4. Step
  4. 4. 예시
  5. 5. 추가적인 용어정리 및 팁
    1. 5.1. 용어정리
    2. 5.2.