0JUUU / spring-batch

Spring Boot 기반으로 개발하는 Spring Batch
1 stars 0 forks source link

섹션 6. 스프링 배치 실행 - Flow #10

Open 0JUUU opened 2 years ago

0JUUU commented 2 years ago
0JUUU commented 2 years ago

FlowJob - 개념 및 API 소개

    @Bean
    public Job batchJob() {
        return jobBuilderFactory.get("batchJob")
                                .start(step1())
                                    .on("COMPLETED").to(step2())
                                .from(step1())
                                    .on("FAILED").to(step3())
                                .end()
                                .build();
    }

FlowJob - 성공

image

FlowJob - Step1 실패

BATCH_JOB_EXECUTION

image

BATCH_STEP_EXECUTION

image
0JUUU commented 2 years ago

FlowJob - start() / next()

start()

next()

의도적인 에러 발생

BATCH_JOB_EXECUTION

image

BATCH_STEP_EXECUTION

image
0JUUU commented 2 years ago

Transition - 배치상태 유형 (BatchStatus / ExitStatus / FlowExecutionStatus)

BatchStatus (Enum 형태로 정의)

ExitStatus

FlowExecutionStatus

image

@RequiredArgsConstructor
@Configuration
public class TransitionConfiguration {

    private final JobBuilderFactory jobBuilderFactory;
    private final StepBuilderFactory stepBuilderFactory;

    @Bean
    public Job batchJob() {
        return jobBuilderFactory.get("batchJob")
                                .start(step1())
                                .next(step2())
                                .build();
    }
@Bean
    public Step step1() {
        return stepBuilderFactory.get("step1")
                                 .tasklet(new Tasklet() {
                                     @Override
                                     public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
                                         System.out.println(">> step1 has executed");
                                         return RepeatStatus.FINISHED;
                                     }
                                 }).build();
    }

    @Bean
    public Step step2() {
        return stepBuilderFactory.get("step2")
                                 .tasklet((contribution, chunkContext) -> {
                                     System.out.println(">> step2 has executed");
                                     contribution.setExitStatus(ExitStatus.FAILED);
                                     return RepeatStatus.FINISHED;
                                 }).build();
    }
}

BATCH_JOB_EXECUTION

image

BATCH_STEP_EXECUTION

image

FlowJob

0JUUU commented 2 years ago

Transition - on() / to() / stop(), fail(), end(), stopAndRestart()

Transition

Transition - API

Job을 중단하거나 종료하는 Transition API

image

image

    @Bean
    public Job batchJob() {
        return this.jobBuilderFactory.get("batchJob")
                                     .start(step1())
                                         .on("FAILED")
                                         .to(step2())
                                         .on("FAILED")
                                         .stop()
                                     .from(step1())
                                         .on("*")
                                         .to(step3())
                                         .next(step4())
                                     .from(step2())
                                         .on("*")
                                         .to(step5())
                                     .end()
                                     .build();
    }

결과

image

FAILED 되도록 코드 변경

// step1()
contribution.setExitStatus(ExitStatus.FAILED);
0JUUU commented 2 years ago

사용자 정의 ExitStatus

    @Bean
    public Job batchJob() {
        return jobBuilderFactory.get("batchJob")
            .start(step1())
                .on("FAILED")
                .to(step2())
                .on("PASS")
                .stop()
            .end()
            .build();
    }

BATCH_JOB_EXECUTION

image

BATCH_STEP_EXECUTION

image

사용자 정의 Status

public class PassCheckingListener implements StepExecutionListener {

    @Override
    public void beforeStep(StepExecution stepExecution) {
    }

    @Override
    public ExitStatus afterStep(StepExecution stepExecution) {
        String exitCode = stepExecution.getExitStatus().getExitCode();
        if (!exitCode.equals(ExitStatus.FAILED.getExitCode())) {
            return new ExitStatus("PASS");
        }
        return null;
    }
}

BATCH_JOB_EXECUTION

image

BATCH_STEP_EXECUTION

image
0JUUU commented 2 years ago

JobExecutionDecider

🤔 왜 내가 정의한 ExitStatus & Listener의 조합보다 JobExecutionDecider 방식이 더 좋을까?

public class CustomDecider implements JobExecutionDecider {

    private int count = 0;

    @Override
    public FlowExecutionStatus decide(JobExecution jobExecution, StepExecution stepExecution) {
        count++;
        System.out.println(">> count " + count);
        if(count % 2 == 0) {
            return new FlowExecutionStatus("EVEN");
        }
        return new FlowExecutionStatus("ODD");
    }
}
image
private int count = 1;
image image
0JUUU commented 2 years ago

FlowJob 아키텍처

image

0JUUU commented 2 years ago

SimpleFlow - 개념 및 API 소개

image

0JUUU commented 2 years ago

SimpleFlow 예제

image

@RequiredArgsConstructor
@Configuration
public class SimpleFlowConfiguration {

    private final JobBuilderFactory jobBuilderFactory;
    private final StepBuilderFactory stepBuilderFactory;

    @Bean
    public Job job() {
        return jobBuilderFactory.get("batchJob")
            .start(flow1())
                .on("COMPLETED")
                .to(flow2())
            .from(flow1())
                .on("FAILED")
                .to(flow3())
            .end()
            .build();
    }

    @Bean
    public Flow flow1() {
        FlowBuilder<Flow> flowBuilder = new FlowBuilder<>("flow1");
        flowBuilder.start(step1())
            .next(step2())
            .end();
        return flowBuilder.build();
    }

    @Bean
    public Flow flow2() {
        FlowBuilder<Flow> flowBuilder = new FlowBuilder<>("flow2");
        flowBuilder.start(flow3())
                   .next(step5())
                   .next(step6())
                   .end();
        return flowBuilder.build();
    }

    @Bean
    public Flow flow3() {
        FlowBuilder<Flow> flowBuilder = new FlowBuilder<>("flow3");
        flowBuilder.start(step3())
                   .next(step4())
                   .end();
        return flowBuilder.build();
    }

    @Bean
    public Step step1() {
        return stepBuilderFactory.get("step1")
                                 .tasklet((contribution, chunkContext) -> {
                                     System.out.println(">> step1 has executed");
//                    throw new RuntimeException("step1 was failed");
                                     return RepeatStatus.FINISHED;
                                 }).build();
    }

    @Bean
    public Step step2() {
        return stepBuilderFactory.get("step2")
                                 .tasklet((contribution, chunkContext) -> {
                                     System.out.println(">> step2 has executed");
                                     return RepeatStatus.FINISHED;
                                 }).build();
    }

    @Bean
    public Step step3() {
        return stepBuilderFactory.get("step3")
                                 .tasklet((contribution, chunkContext) -> {
                                     System.out.println(">> step3 has executed");
                                     return RepeatStatus.FINISHED;
                                 }).build();
    }

    @Bean
    public Step step4() {
        return stepBuilderFactory.get("step4")
                                 .tasklet((contribution, chunkContext) -> {
                                     System.out.println(">> step4 has executed");
                                     return RepeatStatus.FINISHED;
                                 }).build();
    }

    @Bean
    public Step step5() {
        return stepBuilderFactory.get("step5")
                                 .tasklet((contribution, chunkContext) -> {
                                     System.out.println(">> step5 has executed");
                                     return RepeatStatus.FINISHED;
                                 }).build();
    }

    @Bean
    public Step step6() {
        return stepBuilderFactory.get("step6")
                                 .tasklet((contribution, chunkContext) -> {
                                     System.out.println(">> step6 has executed");
                                     return RepeatStatus.FINISHED;
                                 }).build();
    }
}
image

BATCH_JOB_EXECUTION

image

BATCH_STEP_EXECUTION

image

일부러 예외 발생 (step1에서 실패 -> Flow 3로 감)

image image

BATCH_JOB_EXECUTION

image

BATCH_STEP_EXECUTION

image
0JUUU commented 2 years ago

SimpleFlow 아키텍처

image

image

image

0JUUU commented 2 years ago

FlowStep

image

@RequiredArgsConstructor
@Configuration
public class FlowStepConfiguration {

    private final JobBuilderFactory jobBuilderFactory;
    private final StepBuilderFactory stepBuilderFactory;

    @Bean
    public Job job() {
        return jobBuilderFactory.get("batchJob")
                                .start(flowStep())
                                .next(step2())
                                .build();
    }

    private Step flowStep() {
        return stepBuilderFactory.get("flowStep")
            .flow(flow())
            .build();
    }

    private Flow flow() {
        FlowBuilder<Flow> flowBuilder = new FlowBuilder<>("flow");
        flowBuilder.start(step1())
            .end();
        return flowBuilder.build();
    }

    @Bean
    public Step step1() {
        return stepBuilderFactory.get("step1")
                                 .tasklet(new Tasklet() {
                                     @Override
                                     public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
                                         System.out.println("step1 was executed");
                                         throw new RuntimeException("step1 was failed");
//                        return RepeatStatus.FINISHED;
                                     }
                                 })
                                 .build();
    }

    @Bean
    public Step step2() {
        return stepBuilderFactory.get("step2")
                                 .tasklet(new Tasklet() {
                                     @Override
                                     public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
                                         System.out.println("step2 was executed");
                                         return RepeatStatus.FINISHED;
                                     }
                                 })
                                 .build();
    }
}

BATCH_JOB_EXECUTION

image

BATCH_STEP_EXECUTION

image
0JUUU commented 2 years ago

@JobScope / @StepScope - 기본개념 및 설정

Scope

Spring Batch Scope

@JobScpe, @StepScope

CustomJobListener

public class CustomJobListener implements JobExecutionListener {

    @Override
    public void beforeJob(JobExecution jobExecution) {
        jobExecution.getExecutionContext().putString("name", "user1");
    }

    @Override
    public void afterJob(JobExecution jobExecution) {
    }
}

CustomStepListener

public class CustomStepExecutionListener implements StepExecutionListener {

    @Override
    public void beforeStep(StepExecution stepExecution) {
        stepExecution.getExecutionContext().putString("name2", "user2");
    }

    @Override
    public ExitStatus afterStep(StepExecution stepExecution) {
        return null;
    }
}

Configuration

@RequiredArgsConstructor
@Configuration
public class JobScope_StepScope_Configuration {
    private final JobBuilderFactory jobBuilderFactory;
    private final StepBuilderFactory stepBuilderFactory;

    @Bean
    public Job job() {
        return jobBuilderFactory.get("batchJob")
                                .start(step1(null))
                                .next(step2())
                                .listener(new CustomJobListener())
                                .build();
    }

    @Bean
    @JobScope
    public Step step1(@Value("#{jobParameters['message']}") String message) {
        System.out.println("jobParameters['message'] : " + message);
        return stepBuilderFactory.get("step1")
                                 .tasklet(tasklet1(null))
                                 .build();
    }

    @Bean
    public Step step2() {
        return stepBuilderFactory.get("step2")
                                 .tasklet(tasklet2(null))
                                 .listener(new CustomStepExecutionListener())
                                 .build();
    }

    @Bean
    @StepScope
    public Tasklet tasklet1(@Value("#{jobExecutionContext['name']}") String name) {
        System.out.println(">> jobExecutionContext['name'] = " + name);
        return (stepContribution, chunkContext) -> {
            System.out.println(">> tasklet1 has executed");
            return RepeatStatus.FINISHED;
        };
    }

    @Bean
    @StepScope
    public Tasklet tasklet2(@Value("#{stepExecutionContext['name2']}") String name2) {
        System.out.println(">> stepExecutionContext['name2'] = " + name2);
        return (stepContribution, chunkContext) -> {
            System.out.println(">> tasklet2 has executed");
            return RepeatStatus.FINISHED;
        };
    }
}
image

@JobScope를 생략한다면? ➡️ 에러 발생

image
0JUUU commented 2 years ago

@JobScope / @StepScope 아키텍처

Proxy 객체 생성

🤔 왜 Proxy 객체를 사용하지?

JobScope, StepScope

JobContext, StepContext

image