Spring batch :Restart a job and then start next job automatically

前端 未结 3 397
时光说笑
时光说笑 2020-12-21 01:12

I need to create a recovery pattern. In my pattern I can launch a job only on a given time window. In case the job fails, it will only be restarted on the next time window a

相关标签:
3条回答
  • 2020-12-21 01:45

    Just to recap what was actually done based on the advice provided by incomplete-co.de. I created a recovery flow which is similar to the one below. The recovery flow wraps my actual batch and responsible only to serve the correct job parameters to the internal job. It could be initial parameters on first execution, new parameters on normal execution or old parameters in case the last execution had failed.

    <batch:job id="recoveryWrapper"
           incrementer="wrapperRunIdIncrementer"
           restartable="true">
        <batch:decision id="recoveryFlowDecision" decider="recoveryFlowDecider">
            <batch:next on="FIRST_RUN" to="defineParametersOnFirstRun" />
            <batch:next on="RECOVER" to="recover.batchJob " />
            <batch:next on="CURRENT" to="current.batchJob " />
        </batch:decision>
        <batch:step id="defineParametersOnFirstRun" next="current.batchJob">
            <batch:tasklet ref="defineParametersOnFirstRunTasklet"/>
        </batch:step>
        <batch:step id="recover.batchJob " next="current.batchJob">
            <batch:job ref="batchJob" job-launcher="jobLauncher"
                                job-parameters-extractor="jobParametersExtractor" />
        </batch:step>
        <batch:step id="current.batchJob" >
           <batch:job ref="batchJob" job-launcher="jobLauncher"
                                job-parameters-extractor="jobParametersExtractor" />
        </batch:step>
    </batch:job>
    

    The heart of the solution is the RecoveryFlowDecider the JobParametersExtractor while using Spring Batch Restart mechanism. RecoveryFlowDecider will query the JobExplorer and JobRepository to find out if we had a failure in the last run. It will place The last execution on the execution context of the wrapper to use later in the JobParametersExtractor. Note the use of runIdIncremeter to allow re-execution of the wrapper job.

    @Component
     public class RecoveryFlowDecider implements JobExecutionDecider {
            private static final String FIRST_RUN = "FIRST_RUN";
            private static final String CURRENT = "CURRENT";
            private static final String RECOVER = "RECOVER";
    
            @Autowired
            private JobExplorer jobExplorer;
            @Autowired
            private JobRepository jobRepository;
    
            @Override
            public FlowExecutionStatus decide(JobExecution jobExecution
                                             ,StepExecution stepExecution) {
                // the wrapper is named as the wrapped job + WRAPPER
                String wrapperJobName = jobExecution.getJobInstance().getJobName();
                String jobName;
                 jobName = wrapperJobName.substring(0,wrapperJobName.indexOf(EtlConstants.WRAPPER));
                List<JobInstance> instances = jobExplorer.getJobInstances(jobName, 0, 1);
                    JobInstance internalJobInstance = instances.size() > 0 ? instances.get(0) : null;
    
                if (null == internalJobInstance) {
                    return new FlowExecutionStatus(FIRST_RUN);
                }
                JobExecution lastExecution = jobRepository.getLastJobExecution(internalJobInstance.getJobName()
        ,internalJobInstance.getJobParameters());
                //place the last execution on the context (wrapper context to use later)
                jobExecution.getExecutionContext().put(EtlConstants.LAST_EXECUTION, lastExecution);
                    ExitStatus exitStatus = lastExecution.getExitStatus();
    
                if (ExitStatus.FAILED.equals(exitStatus) || ExitStatus.UNKNOWN.equals(exitStatus)) {
                    return new FlowExecutionStatus(RECOVER);
                }else if(ExitStatus.COMPLETED.equals(exitStatus)){
                    return new FlowExecutionStatus(CURRENT);    
                }
                //We should never get here unless we have a defect
                throw new RuntimeException("Unexpecded batch status: "+exitStatus+" in decider!");
    
            }
    
    }
    

    Then the JobParametersExtractor will test again for the outcome of the last execution, in case of failed job it will serve the original parameters used to execute the failed job triggering Spring Bacth restart mechanism. Otherwise it will create a new set of parameters and will execute at his normal course.

        @Component
        public class JobExecutionWindowParametersExtractor implements
                JobParametersExtractor {
            @Override
            public JobParameters getJobParameters(Job job, StepExecution stepExecution) {
    
                // Read the last execution from the wrapping job
                // in order to build Next Execution Window
                JobExecution lastExecution= (JobExecution) stepExecution.getJobExecution().getExecutionContext().get(EtlConstants.LAST_EXECUTION);;
    
                if(null!=lastExecution){
                    if (ExitStatus.FAILED.equals(lastExecution.getExitStatus())) {
                        JobInstance instance = lastExecution.getJobInstance();
                        JobParameters parameters = instance.getJobParameters();                 
                                    return parameters;
                    }
                }       
                //We do not have failed execution or have no execution at all we need to create a new execution window
                return buildJobParamaters(lastExecution,stepExecution);
            }
    ...
    }
    
    0 讨论(0)
  • 2020-12-21 01:47

    have you considered a JobStep? that is, a step determines if there are any additional jobs to be run. this value is set into the StepExecutionContext. a JobExecutionDecider then checks for this value; if exists, directs to a JobStep which launches the Job.

    here's the doc on it http://docs.spring.io/spring-batch/reference/htmlsingle/#external-flows

    0 讨论(0)
  • 2020-12-21 02:06

    Is it possible to do it in the opposite manner?

    In every time window, submit the job intended for that time window.

    However, the very first step of the job should check whether the job in previous time window is completed successfully or not. If it failed before, then submit the previous job, and wait for completion, before going into its own logic.

    0 讨论(0)
提交回复
热议问题