Spring Batch Step 파헤쳐보기

Step 개념

  • Batch Job 을 구성하는 독립적인 단계로 하나의 Job은 하나 이상의 Step으로 구성됩니다.
  • Spring Batch Job 구현체중 하나인 SimpleJob을 까보면 Job 은 내부 멤버변수로 Step List를 들고 있음을 확인할 수 있습니다.
    1
    2
    3
    4
    public class SimpleJob extends AbstractJob {
    private List<Step> steps = new ArrayList<>();
    //...
    }
    [2] Spring Batch Step

StepExecution 이란?

  • Step에 대한 한번의 시도를 의미하는 객체로서 Step 실행 중에 발생한 정보들을 저장하고 있는 객체입니다.
  • DB의 BATCH_STEP_EXECUTION 테이블과 1:1로 매핑됩니다.
  • Job이 실패해서 재수행하는 경우에는 실패한 Step에 대해서만 재시작하고, 성공한 Step은 생략합니다. ( 하지만 allowStartIfComplete 설정값을 변경하면 성공한 Step도 재시작되게 변경할 수 있습니다 )

예를 들어 Step 1,2,3이 존재하는데 Step2번이 실행중에 실패한다면 Step 1은 성공 , Step 2는 실패처리 됩니다. 그리고 Step 3는 실행되지 않습니다.
이 상태에서 Job을 재시작하면 Step2부터 재시작하여 Step2,Step3 가 수행됩니다.

  • Job을 구성하는 모든 Step의 실행 정보인 StepExecution이 완료 처리되어야만 JobExecution이 완료처리됩니다.

즉 Spring Batch 도메인 용어를 정리하면 하나의 Job은 여러개의 Step으로 구성되고, Job이 JobParameter를 주입받아 실행되는 객체가 JobInstance객체입니다.

JobInstance를 실행한 정보가 JobExecution이고, Job이 실행되면서 Job을 구성하는 Step들의 실행정보가 StepExecution입니다.

[1] Spring Batch Job과 Step에 관련된 도메인 객체들

Step간 데이터 공유하기 - ExecutionContext

  • ExecutionContext를 활용하면 Job내 Step간 데이터를 공유할 수 있습니다. 혹은 실패한 Step에서 Step 재시작시 실패 이전까지 작업했던 상태값들을 가져올 수 있습니다.
  • ExecutionContext는 Spring Batch에서 관리하는 key-value (Map) 컬렉션입니다.
  • StepExecution, JobExecution 객체의 멤버변수로 선언되고 , 각각 DB의 BATCH_JOB_EXECUTION_CONTEXT , BATCH_STEP_EXECUTION_CONTEXT 테이블에 1:1 매핑됩니다.
  • StepExecutionExecutionContext는 Step안에서만 공유됩니다. 즉 특정 Step에서만 접근이 가능합니다. 실패한 Step이 재시작된 경우도 이전까지 작업한 내용을 불러들일수 있습니다.
  • JobExeuctionExecutionContext는 모든 Step안에서 공유됩니다.
ExecutionContext

예시 코드

아래와 같이 ExecutionContextStepContribution 또는 ChunkContext를 통해 접근하고, 값을 넣어줄 수 있습니다.
넣어준 값은 BATCH_JOB_EXECUTION_CONTEXT , BATCH_STEP_EXECUTION_CONTEXT 테이블에 각각 직렬화되어 저장됩니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class ExecutionContextTasklet1 implements Tasklet {

@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) {

ExecutionContext jobExecutionContext = contribution.getStepExecution().getJobExecution().getExecutionContext();
ExecutionContext stepExecutionContext = contribution.getStepExecution().getExecutionContext();

jobExecutionContext.put("jobName", "developer"); //1. BATCH_JOB_EXECUTION_CONTEXT 에 저장 (모든 STEP에서 공유)
stepExecutionContext.put("stepName", "software"); // 2. BATCH_STEP_EXECUTION_CONTEXT 에 저장 (특정 STEP에서 공유)

return RepeatStatus.FINISHED;
}
}
BATCH_JOB_EXECUTION_CONTEXT 테이블
BATCH_STEP_EXECUTION_CONTEXT 테이블

Spring Batch에서 제공하는 Step 구현체(5)

  • Step 인터페이스를 AbstractStep이라는 추상 클래스에서 구현하고,AbstractStep 추상클래스를 구현하는 구조입니다.
  • Batch에서 제공하는 Step의 구현체는 아래와 같은 5개의 구현체가 존재합니다.
  1. Tasklet Step
  2. Partition Step
  3. Job Step
  4. Flow Step
  5. Decision Step
Spring Batch에서 제공하는 Step 구현체 종류

TaskletStep

  • RepeatTemplate을 사용해서 Tasklet 코드 block을 트랜잭션 경계 내(성공시 커밋,실패시 롤백)에서 반복해서 실행합니다.
    언제까지 반복해서 실행할것인가에 대한 판단은 Tasklet 객체에서 반환하는 RepeatStatus값에 의해 결정됩니다. RepeatStatus.FINISHED 와 같이 특정 RepeatStatus를 반환할떄까지 계속해서 실행합니다.

  • TaskletStep은 아래와 같은 tasklet 인터페이스를 구현하는 tasklet 구현체를 멤버변수로 가지고 있습니다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    /**
    * Strategy for processing in a step.
    */
    public interface Tasklet {

    /**
    * @return an {@link RepeatStatus} indicating whether processing is
    * continuable. Returning {@code null} is interpreted as {@link RepeatStatus#FINISHED}
    *
    * @throws Exception thrown if error occurs during execution.
    */
    @Nullable
    RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception;

    }
  • TaskletStep의 코드를 확인해보면 Tasklet 호출횟수를 조정하기 위한 RepeatTemplate 와 실행되야할 작업 그 자체 (tasklet) 구현체를 멤버변수로 들고있는것을 확인할 수 있습니다.

1
2
3
4
5
6
7
public class TaskletStep extends AbstractStep {

private RepeatOperations stepOperations = new RepeatTemplate();
//...
private Tasklet tasklet;
//...
}
  • 자주 사용되는 tasklet 구현체는 Spring Batch에서 이미 구현해놓았습니다. 이 중 ChunkOrientedTasklet구현체을 활용해 Chunk 단위로 배치 작업을 쪼개서 처리할 수 있습니다.
    Spring Batch에서 제공하는 Step 구현체 종류
Read more

Spring Batch Job의 구성

Spring Batch Job

  • 하나의 논리적인 배치작업을 의미합니다.
  • 배치작업을 어떻게 구성하고, 실행할것인지 명세해놓은 객체입니다.
  • 배치 작업을 구성하기 위한 최상위 인터페이스이며, Spring Batch가 기본 구현체를 제공합니다.
  • 여러 Step으로 구성될 수 있으며, 반드시 하나 이상의 Step으로 구성해야 합니다.
    ( Job - Step의 관계는 일대다관계입니다)

Job의 구현체

  • Spring Batch에서 제공하는 Job의 구현체는 2가지 종류가 있습니다.
  1. Simple Job : 순차적으로 Step을 실행시키는 Job을 의미합니다.
  2. Flow Job : 조건별로 분기를 따라 Step을 구성하여 실행시키는 Job을 의미합니다.

Job 구성방법

  • Spring Batch에서는 Job과 Step을 Spring Bean으로 관리한다. 때문에 @Configuration 설정 클래스에서 Job 객체를 등록해줄 수 있습니다.
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
@Bean
public Job helloJob() { // 1. Job 구성
return jobBuilderFactory.get("helloJob")
.start(helloStep1())
.next(helloStep2())
.build();
}
@Bean
public Step helloStep1() {// 2. Step 구성
return stepBuilderFactory.get("helloStep1")
.tasklet(((contribution, chunkContext) -> {
System.out.println("##### step1");
return RepeatStatus.FINISHED;
}))
.build();
}
@Bean
public Step helloStep2() {
return stepBuilderFactory.get("helloStep2")
.tasklet(((contribution, chunkContext) -> {
System.out.println("###### step2");
return RepeatStatus.FINISHED;
}))
.build();
}

Job Launcher 와 Job Parameter

  • JobLaunder 배치 작업을 실행하는 역할을 합니다.
  • JobParameter와 Job을 인자로 받아서 실행합니다.
[1] Job Launder Sequence Diagram
  • 예시코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Component
public class JobRunner implements ApplicationRunner {

private final Job job;
private final JobLauncher jobLauncher;

public JobRunner(Job job, JobLauncher jobLauncher) {
this.job = job;
this.jobLauncher = jobLauncher;
}

@Override
public void run(ApplicationArguments args) throws Exception {
JobParameters jobParameters = new JobParametersBuilder() // 1. Job Parameter를 생성합니다.
.addString("name", "user1")
.toJobParameters();

jobLauncher.run(job,jobParameters); // 2. Job Launcher가 주어진 Job과 Job Parameter를 받아서 Job을 실행합니다.

}
}

Job Instance

  • Job이 실행될떄 생성되는 Job의 논리적 실행 단위 객체. 즉 작업 실행을 의미한다.
  • Job과 JobInstance는 왜 구분해야 할까?
    • Job의 설정과 구성은 동일하지만 Job이 실행되는 시점에 처리하는 내용은 다르기 때문에 Job의 실행을 구분해야 합니다.
    • JobInstance는 Job과 JobParameter의 조합으로 생성하며, 처음 시작하는 Job과 Job Parameter의 경우는 새로운 Job Instance를 생성합니다.
    • 하나의 Job은 여러번 실행될 수 있으므로 Job과 JobInstance의 연관관계는 1:N의 관계입니다.
  • BATCH_JOB_INSTANCE 테이블에 매핑됩니다.
Job Runner에 의해 Job 실행시 BATCH_JOB_EXECUTION 테이블
  • JobInstance의 경우 동일한 Job과 Job Parameter의 조합인 경우 예외(JobInstanceAlreadyCompleteException)를 던집니다.즉 동일한 Job을 동일한 Job Parameter로 돌리면 중복 JobInstance로 판정됩니다.
    1
    2
    3
    4
    5
    6
    Caused by: org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException: A job instance already exists and is complete for parameters={name=user1}.  If you want to run this job again, change the parameters.
    at org.springframework.batch.core.repository.support.SimpleJobRepository.createJobExecution(SimpleJobRepository.java:139) ~[spring-batch-core-4.3.9.jar:4.3.9]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]

Job Parameter

  • 말그대로 Job을 실행할 때 함께 포함되어 사용되는 parameter 가진 도메인 객체입니다
  • 하나의 Job안에 존재하는 여러 JobInstance를 구분하기 위한 용도이다. 즉 JobInstance와 JobParameter는 1:1 관계입니다. (테이블 관계에서는 1:N)
  • BATCH_JOB_EXECUTION_PARAM 테이블과 매핑됩니다.
  • Spring Batch에서는 STRING, DATE, LONG, DOUBLE 타입을 지원을 하며, BATCH_JOB_EXECUTION_PARAM 테이블에서 타입별로 칼럼을 가지고 있습니다.

Job Parameter 생성방식

  1. Application 생성 시점에 외부 환경변수로 주입되는 방법

빌드한 Jar파일에 다음과 같이 외부 매개변수로 KEY-VALUE 형태로 값을 주입해줄 수 있다. 이떄 문자형이 아닌 경우는 별도로 (타입명)을 지정해주어야 합니다. 혹은 program argument로 key=value형태로 넣어줄 수 있습니다.

1
java -jar spring-batch-0.0.1-SNAPSHOT.jar name=cs seq\(long\)=2L date\(date\)=2023/10/27 age\(double\)=3 
  1. Application Code에서 직접 생성하는 방법
    1
    2
    3
    4
    5
    6
    JobParameters parameters = new JobParametersBuilder()
    .addString("name", "chansoo")
    .addLong("seq", 1l)
    .addDate("date", new Date())
    .addDouble("age", 16.5)
    .toJobParameters();
Job Runner에 의해 Job 실행시 BATCH_JOB_EXECUTION_PARAMS 테이블
  • 값을 주지 않으면 DATE_VAL의 경우는 기본값으로 1970-01-01 09:00:00, STRING_VAL의 경우 빈 문자열('') , LONG & DOUBLE_VAL의 경우는 0이 들어갑니다.
  1. SpEL 문법을 통해 생성하는 방법

Job Parameter를 꺼내오는 법

  1. Tasklet Based Step
  • tasklet의 경우는 StepContribution, ChunkContext 클래스에서 모두 JobParameter를 꺼내올 수 있습니다.
  • 차이점은 StepContribution은 JobParameters 타입의 객체를 반환하고, ChunkContext의 경우 Map 타입의 객체를 반환하는 차이점이 있습니다.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    @Bean
    public Step step1() {
    return stepBuilderFactory.get("step1")
    .tasklet((contribution, chunkContext) -> {
    JobParameters params = contribution.getStepExecution().getJobExecution()
    .getJobParameters();
    String name = params.getString("name");
    Long seq = params.getLong("seq");
    Date date = params.getDate("date");
    Double age = params.getDouble("age");

    Map<String, Object> jobParameters = chunkContext.getStepContext()
    .getJobParameters();
    return RepeatStatus.FINISHED;
    })
    .build();
    }
Read more

spring batch (1)

배치 처리란?

  • 일반적인 GUI를 가진 app은 사용자와 상호작용함
  • 사용자와 상호작용없이 유한한 양의 데이터를 처리하는 것 (GUI없음)
Read more