반응형
728x90
반응형
Job 생성
import lombok.RequiredArgsConstructor;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/*
--job.name=flowTestJob
*/
@Configuration
@RequiredArgsConstructor
public class FlowJobTestConfiguration {
// job 생성
private final JobBuilderFactory jobBuilderFactory;
private final StepBuilderFactory stepBuilderFactory;
@Bean
public Job flowTestJob() {
return this.jobBuilderFactory.get("flowTestJob")
.start(flowTestStep1())
.on("COMPLETED")
.to(flowTestStep3())
.from(flowTestStep1())
.on("FAILED")
.to(flowTestStep2())
.end()
.build();
}
@Bean
public Step flowTestStep1() {
return stepBuilderFactory.get("flowTestStep1")
.tasklet(new Tasklet() {
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
System.out.println("flowTestStep1");
// 고의 에러 발생
throw new RuntimeException("step1 was failed");
// return RepeatStatus.FINISHED;
}
})
.build();
}
@Bean
public Step flowTestStep2() {
return stepBuilderFactory.get("flowTestStep2")
.tasklet(new Tasklet() {
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
System.out.println("flowTestStep2");
return RepeatStatus.FINISHED;
}
})
.build();
}
@Bean
public Step flowTestStep3() {
return stepBuilderFactory.get("flowTestStep3")
.tasklet(new Tasklet() {
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
System.out.println("flowTestStep3");
return RepeatStatus.FINISHED;
}
})
.build();
}
}
1) flowTestJob()을 보자.
@Bean
public Job flowTestJob() {
return this.jobBuilderFactory.get("flowTestJob")
/* 각각의 스텝은 독립적으로 생성되어 Tasklet 이 생성된다.
* Step 은 자기만의 Tasklet 을 가진다. */
.start(flowTestStep1())
// 위 스텝이 성공하게되면, step3 번을 실행시키고,
// 위 스텝이 실패하게되면, step2 번을 실행시켜보자.
.on("COMPLETED")
.to(flowTestStep3())
.from(flowTestStep1())
// STEP1 : FAILED, STEP2 : COMPLETED
.on("FAILED")
.to(flowTestStep2()) // step1 이 실패했어도 JOB은 COMPLETED
.end()
.build();
}
실행흐름
Step | String pattern | Next Step |
flowTestStep1() | COMPLETED | flowTestStep3() |
FAILED | flowTestStep2() |
flowTestStep1()을 고의로 에러를 발생시키자.
@Bean
public Step flowTestStep1() {
return stepBuilderFactory.get("flowTestStep1")
.tasklet(new Tasklet() {
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
System.out.println("flowTestStep1");
// 고의 에러 발생
throw new RuntimeException("step1 was failed");
// return RepeatStatus.FINISHED;
}
})
.build();
}
실행결과
flowTestStep1
flowTestStep2
실행흐름
위에서 정리했던 실행흐름을 보자.
Step | String pattern | Next Step |
flowTestStep1() | COMPLETED | flowTestStep3() |
FAILED | flowTestStep2() |
flowTestStep1()의 EXIT_CODE가 FAILED이기 때문에 flowTestStep2()가 수행되었다.
DB 테이블 조회
1) BATCH_JOB_EXECUTION
COLUMN | VALUE |
STATUS | COMPLETED |
EXIT_CODE | COMPLETED |
JOB_EXECUTION_ID | 8 |
2) BATCH_JOB_INSTANCE
COLUMN | VALUE |
JOB_NAME | flowTestJob |
3) BATCH_STEP_EXECUTION
- flowTestStep1
COLUMN | VALUE |
STEP_NAME | flowTestStep1 |
EXIT_CODE | FAILED |
STATUS | ABANDONED |
JOB_EXECUTION_ID | 8 |
- flowTestStep2
COLUMN | VALUE |
STEP_NAME | flowTestStep2 |
EXIT_CODE | COMPLETED |
STATUS | COMPLETED |
JOB_EXECUTION_ID | 8 |
flowTestStep1()을 다시 정상 수행시키자.
@Bean
public Step flowTestStep1() {
return stepBuilderFactory.get("flowTestStep1")
.tasklet(new Tasklet() {
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
System.out.println("flowTestStep1");
// 고의 에러 발생
// throw new RuntimeException("step1 was failed");
return RepeatStatus.FINISHED;
}
})
.build();
}
실행결과
flowTestStep1
flowTestStep3
실행흐름
위에서 정리했던 실행흐름을 보자.
Step | String pattern | Next Step |
flowTestStep1() | COMPLETED | flowTestStep3() |
FAILED | flowTestStep2() |
flowTestStep1()의 EXIT_CODE가 COMPLETED 이므로 flowTestStep3()이 실행되었다.
DB 테이블 조회
1) BATCH_JOB_EXECUTION
COLUMN | VALUE |
STATUS | COMPLETED |
EXIT_CODE | COMPLETED |
JOB_EXECUTION_ID | 10 |
2) BATCH_JOB_INSTANCE
COLUMN | VALUE |
JOB_NAME | flowTestJob |
3) BATCH_STEP_EXECUTION
- flowTestStep1
COLUMN | VALUE |
STEP_NAME | flowTestStep1 |
EXIT_CODE | COMPLETED |
STATUS | COMPLETED |
JOB_EXECUTION_ID | 10 |
- flowTestStep3
COLUMN | VALUE |
STEP_NAME | flowTestStep3 |
EXIT_CODE | COMPLETED |
STATUS | COMPLETED |
JOB_EXECUTION_ID | 10 |
Step 오류 발생시키기 - 다른 방법
1) throw new Exception()
@Bean
public Step flowTestStep1() {
return stepBuilderFactory.get("flowTestStep1")
.tasklet(new Tasklet() {
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
System.out.println("flowTestStep1");
// 고의 에러 발생
throw new RuntimeException("step1 was failed");
// contribution.setExitStatus(ExitStatus.FAILED);
// return RepeatStatus.FINISHED;
}
})
.build();
}
BATCH_STEP_EXECUTION
COLUMN | VALUE |
STEP_NAME | flowTestStep1 |
EXIT_CODE | ABANDONED |
STATUS | FAILED |
JOB_EXECUTION_ID | 10 |
2) EXIT_STATUS를 직접 설정
public Step flowTestStep1() {
return stepBuilderFactory.get("flowTestStep1")
.tasklet(new Tasklet() {
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
System.out.println("flowTestStep1");
contribution.setExitStatus(ExitStatus.FAILED);
return RepeatStatus.FINISHED;
}
})
.build();
}
BATCH_STEP_EXECUTION
COLUMN | VALUE |
STEP_NAME | flowTestStep1 |
EXIT_CODE | FAILED |
STATUS | COMPLETED |
JOB_EXECUTION_ID | 10 |
ABANDONED vs FAILED
ABANDONED | Job 실패 후, 재실행시 수행되지 않고 건너뛴다. |
FAILED | Job 실패 후, 재실행시 수행된다. |
반응형