SpringBatch从入门到实战(三):多步骤控制

一:if else案例

案例:如果开始步骤成功了就执行成功步骤,否则执行失败步骤。

// 伪代码
String exitStatus = helloWorldJob();
if("FAILED".equals(exitStatus)){
    failStep();
}else{
    successStep();
}
@Configuration
public class HelloWorldJobConfig {

    @Autowired
    private JobBuilderFactory jobBuilderFactory;

    @Autowired
    private StepBuilderFactory stepBuilderFactory;


    @Bean
    public Job helloWorldJob() {
        // 执行helloWorldStep,如果成功执行successStep,否则执行failStep(helloWorldStep抛异常时算失败)
        // on()表示条判断
        return jobBuilderFactory.get("helloWorldJob")
                .start(helloWorldStep()).on("FAILED").to(failStep())
                .from(helloWorldStep()).on("*").to(successStep())
                .end()
                .incrementer(new RunIdIncrementer())
                .build();
    }


    @Bean
    public Step helloWorldStep() {
        return stepBuilderFactory.get("helloWorldStep")
                .tasklet(helloWorldTasklet())
                .build();
    }

    @Bean
    public Step successStep() {
        return stepBuilderFactory.get("successStep")
                .tasklet(successTasklet())
                .build();
    }

    @Bean
    public Step failStep() {
        return stepBuilderFactory.get("failStep")
                .tasklet(failTasklet())
                .build();
    }

    @Bean
    public Tasklet helloWorldTasklet() {
        return new Tasklet() {
            @Override
            public RepeatStatus execute(StepContribution stepContribution, ChunkContext chunkContext) throws Exception {
                System.out.println("helloWorldTasklet");
                int a = 1/0;
                return RepeatStatus.FINISHED;
            }
        };
    }


    @Bean
    public Tasklet successTasklet() {
        return new Tasklet() {
            @Override
            public RepeatStatus execute(StepContribution stepContribution, ChunkContext chunkContext) throws Exception {
                System.out.println("successTasklet");
                return RepeatStatus.FINISHED;
            }
        };
    }

    @Bean
    public Tasklet failTasklet() {
        return new Tasklet() {
            @Override
            public RepeatStatus execute(StepContribution stepContribution, ChunkContext chunkContext) throws Exception {
                System.out.println("failTasklet");
                return RepeatStatus.FINISHED;
            }
        };
    }
}
  • start(Step) 后面跟on(), on表示start()返回的退出状态ExitStatus,如果equals就执行to(Step)的逻辑。
  • 后面都是从from(Step)开始,参数表示用那个步骤的返回值与后面的on(String)做比较,eqauls为true执行to(Step)逻辑。* 表示通配符,表示else逻辑。

二:自定义on()值

  • start(Step) 后面跟next(jobExecutionDecider())。
  • from都是取作业执行决策器jobExecutionDecider返回的值与on做比较。
@Bean
public Job helloWorldJob() {
    return jobBuilderFactory.get("helloWorldJob")
            .start(helloWorldStep())
            .next(jobExecutionDecider())
            .from(jobExecutionDecider()).on("OK").to(successStep())
            .from(jobExecutionDecider()).on("*").to(failStep())
            .end()
            .incrementer(new RunIdIncrementer())
            .build();
}


@Bean
public StepExecutionListener stepExecutionListener() {
    return new HelloWorldStepListener();
}


@Bean
public Tasklet helloWorldTasklet() {
    return new Tasklet() {
        @Override
        public RepeatStatus execute(StepContribution stepContribution, ChunkContext chunkContext) throws Exception {
            System.out.println("helloWorldTasklet");
            // 不支持put chunkContext.getStepContext().getJobExecutionContext().put("code", "500");

			// 500 -> ERROR -> failStep
            ExecutionContext executionContext = chunkContext.getStepContext().getStepExecution().getJobExecution().getExecutionContext();
            executionContext.put("code", "500");
            return RepeatStatus.FINISHED;
        }
    };
}
public class MyJobExecutionDecider implements JobExecutionDecider {
    @Override
    public FlowExecutionStatus decide(JobExecution jobExecution, StepExecution stepExecution) {
        String code = jobExecution.getExecutionContext().getString("code");
        if ("200".equals(code)) {
            return new FlowExecutionStatus("OK");
        } else if ("404".equals(code)) {
            return new FlowExecutionStatus("NotFound");
        }
        return new FlowExecutionStatus("ERROR");
    }
}

三:退出状态

3.1 退出状态

public class ExitStatus implements Serializable, Comparable<ExitStatus> {

    //未知状态
	public static final ExitStatus UNKNOWN = new ExitStatus("UNKNOWN");

    //执行中
	public static final ExitStatus EXECUTING = new ExitStatus("EXECUTING");

    //执行完成
	public static final ExitStatus COMPLETED = new ExitStatus("COMPLETED");

    //无效执行
	public static final ExitStatus NOOP = new ExitStatus("NOOP");

    //执行失败
	public static final ExitStatus FAILED = new ExitStatus("FAILED");

    //编程方式主动停止
	public static final ExitStatus STOPPED = new ExitStatus("STOPPED");
}    

一般来说,作业启动之后,这些状态皆为流程自行控制。

  • 顺利结束返回:COMPLETED
  • 异常结束返回:FAILED
  • 无效执行返回:NOOP
  • 编程结束:STOPED

但是也可以通过api强制改变状态:

  • end():作业流程直接成功结束,返回状态为:COMPLETED
  • fail():作业流程直接失败结束,返回状态为:FAILED
  • stopAndRestart(step) :作业流程中断结束,返回状态:STOPPED 再次启动时,从step位置开始执行 (注意:前提是参数与Job Name一样)
//如果helloWorldStep 执行成功:下一步执行successStep 否则是failStep
@Bean
public  Job job(){
    return jobBuilderFactory.get("helloWorldJob")
            .start(helloWorldStep())
            //表示将当前本应该是失败结束的步骤直接转成正常结束--COMPLETED
            //.on("FAILED").end()  
            //表示将当前本应该是失败结束的步骤直接转成失败结束:FAILED
            //.on("FAILED").fail()  
            //表示将当前本应该是失败结束的步骤直接转成停止结束:STOPPED   里面参数表示后续要重启时, 从successStep位置开始
            .on("FAILED").stopAndRestart(successStep())
            .from(firstStep()).on("*").to(successStep())
            .end()
            .incrementer(new RunIdIncrementer())
            .build();
}

3.2 停止作业

方式一: ExitStatus.STOPPED

@Configuration
public class HelloWorldStopJobConfig {

    @Autowired
    private JobBuilderFactory jobBuilderFactory;

    @Autowired
    private StepBuilderFactory stepBuilderFactory;


    @Bean
    public Job helloWorldStopJob() {
        return jobBuilderFactory.get("helloWorldStopJob")
                .start(step1()).on("STOPPED").stopAndRestart(step1())
                .from(step1()).on("*").to(step2())
                .end()
                .incrementer(new RunIdIncrementer())
                .build();
    }


    @Bean
    public Step step1() {
        return stepBuilderFactory.get("step1")
                .tasklet(tasklet1())
                .listener(stepExecutionListener())
                // 允许执行完后,运行重启
                .allowStartIfComplete(true)
                .build();
    }

    @Bean
    public StepExecutionListener stepExecutionListener() {
        return new StopStepListener();
    }


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


    int i = -1;
    @Bean
    public Tasklet tasklet1() {
        return new Tasklet() {
            @Override
            public RepeatStatus execute(StepContribution stepContribution, ChunkContext chunkContext) throws Exception {
                System.out.println("tasklet1");
                if (i < 0) {
                    ExecutionContext executionContext = chunkContext.getStepContext().getStepExecution().getExecutionContext();
                    executionContext.put("flag", "stop");
                }
                return RepeatStatus.FINISHED;
            }
        };
    }


    @Bean
    public Tasklet tasklet2() {
        return new Tasklet() {
            @Override
            public RepeatStatus execute(StepContribution stepContribution, ChunkContext chunkContext) throws Exception {
                System.out.println("tasklet2");
                return RepeatStatus.FINISHED;
            }
        };
    }
}


public class StopStepListener implements StepExecutionListener {

    @Override
    public void beforeStep(StepExecution stepExecution) {
        System.out.println("beforeStep");
    }

    @Override
    public ExitStatus afterStep(StepExecution stepExecution) {
        Object flag = stepExecution.getExecutionContext().get("flag");
        if ("stop".equals(flag)) {
            return ExitStatus.STOPPED;
        }
        return stepExecution.getExitStatus();
    }
}

SpringBatch从入门到实战(三):多步骤控制_第1张图片

在这里插入图片描述
Step1正常的RepeatStatus.FINISHED了所以status=COMPLETED,但是实际退出编码是STOPPED。
在这里插入图片描述

方式二:setTerminateOnly()

StepExecution#setTerminateOnly() 给运行中的stepExecution设置停止标记,Spring Batch 识别后直接停止步骤,进而停止流程。 这种方式更简单。

@Bean
public Job helloWorldStopJob() {
    return jobBuilderFactory.get("helloWorldStopJob")
            .start(step1())
            .next(step2())
            .incrementer(new RunIdIncrementer())
            .build();
}


@Bean
public Step step1() {
    return stepBuilderFactory.get("step1")
            .tasklet(tasklet1())
            // 允许执行完后,运行重启
            .allowStartIfComplete(true)
            .build();
}


int i = -1;
@Bean
public Tasklet tasklet1() {
    return new Tasklet() {
        @Override
        public RepeatStatus execute(StepContribution stepContribution, ChunkContext chunkContext) throws Exception {
            System.out.println("tasklet1");
            if (i < 0) {
                // 终止后
                chunkContext.getStepContext().getStepExecution().setTerminateOnly();
            }
            return RepeatStatus.FINISHED;
        }
    };
}

SpringBatch从入门到实战(三):多步骤控制_第2张图片

在这里插入图片描述

== 注意:使用监听器返回ExitStatus.STOPPED和使StepExecution().setTerminateOnly() 在BATCH_STEP_EXECUTION中的status字段值不一样,前者是COMPLETED后者是STOPPED。==

在这里插入图片描述

四:作业重启

一般情况下当某个作业发生异常或者终止状态时可以重启作业。

  • preventRestart() :设置作业禁止重启,重启报错:JobInstance already exists and is not restartable。
@Bean
public Job helloWorldStopJob() {
    return jobBuilderFactory.get("helloWorldStopJob")
            .preventRestart()
            .start(step1())
            .next(step2())
            .incrementer(new RunIdIncrementer())
            .build();
}
  • 设置步骤最多重启次数:startLimit(num),有些步骤可能是因为网络等问题可以通过重试解决,但如果重启一定次数后还没有解决就不允许无限的去重试。超过最大次数报错:Maximum start limit exceeded for step: step1 StartMax:3
@Bean
public Step step1() {
    return stepBuilderFactory.get("step1")
            .startLimit(3)
            .tasklet(tasklet1())
            .build();
}
  • 设置步骤运行重启 allowStartIfComplete(true),这样在执行步骤时就不是NOOP了。
@Bean
public Step step1() {
    return stepBuilderFactory.get("step1")
            .allowStartIfComplete(true)
            .tasklet(tasklet1())
            .build();
}

你可能感兴趣的:(SpringBatch,springbatch)