需要一种方法来防止不必要的作业参数传播到下一次执行Spring Boot批处理作业

时间:2019-04-18 16:18:49

标签: spring-boot spring-batch

我正在使用Spring Boot 2.1.2和Spring Batch 4.1.1运行批处理应用程序。该应用程序将一个MySQL数据库用于Spring Batch元数据数据源。

首先,我使用以下命令运行作业:

java -jar target/batchdemo-0.0.1-SNAPSHOT.jar -Dspring.batch.job.names=echo com.paypal.batch.batchdemo.BatchdemoApplication myparam1=value1 myparam2=value2

请注意,我要传递两个参数:

  

myparam1 = value1   myparam2 = value2

由于该作业使用RunIdIncrementer,因此该应用程序使用的实际参数记录为:

  

作业:[SimpleJob:[name = echo]]使用以下参数完成:[{myparam2 = value2,run.id = 1,myparam1 = value1}]

接下来,我再次运行作业,这次删除myparam2:

java -jar target/batchdemo-0.0.1-SNAPSHOT.jar -Dspring.batch.job.names=echo com.paypal.batch.batchdemo.BatchdemoApplication myparam1=value1

这次,该作业再次以param2运行:

  

作业:[SimpleJob:[name = echo]]使用以下参数完成:[{myparam2 = value2,run.id = 2,myparam1 = value1}]

这将导致调用业务逻辑,就像我再次将myparam2传递给应用程序一样。

是否有一种方法可以删除job参数并将其不传递给下一个实例?

应用代码:

package com.paypal.batch.batchdemo;

import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.launch.support.RunIdIncrementer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
@EnableBatchProcessing
public class BatchdemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(BatchdemoApplication.class, args);
    }

    @Autowired
    JobBuilderFactory jobBuilder;

    @Autowired
    StepBuilderFactory stepBuilder;

    @Autowired
    ParamEchoTasklet paramEchoTasklet;

    @Bean
    public RunIdIncrementer incrementer() {
        return new RunIdIncrementer();
    }

    @Bean
    public Job job() {
        return jobBuilder.get("echo").incrementer(incrementer()).start(echoParamsStep()).build();
    }

    @Bean
    public Step echoParamsStep() {
        return stepBuilder.get("echoParams").tasklet(paramEchoTasklet).build();
    }
}

package com.paypal.batch.batchdemo;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.batch.core.StepContribution;
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.stereotype.Component;

@Component
public class ParamEchoTasklet implements Tasklet {

    @Override
    public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
        LOGGER.info("ParamEchoTasklet BEGIN");
        chunkContext.getStepContext().getJobParameters().entrySet().stream().forEachOrdered((entry) -> {
            String key = entry.getKey();
            Object value = entry.getValue();
            LOGGER.info("Param {} = {}", key, value);
        });
        LOGGER.info("ParamEchoTasklet END");
        return RepeatStatus.FINISHED;
    }

    private Logger LOGGER = LoggerFactory.getLogger(ParamEchoTasklet.class);
}

我调试了spring批处理和spring引导代码,这就是正在发生的事情。 JobParametersBuilder line 273将最近的先前作业实例中的参数与JobParametersIncrementer添加的所有参数一起添加到nextParameters映射中:

List<JobExecution> previousExecutions = this.jobExplorer.getJobExecutions(lastInstances.get(0));
if (previousExecutions.isEmpty()) {
    // Normally this will not happen - an instance exists with no executions
    nextParameters = incrementer.getNext(new JobParameters());
}
else {
    JobExecution previousExecution = previousExecutions.get(0);
    nextParameters = incrementer.getNext(previousExecution.getJobParameters());
}

然后,由于我使用的是Spring Boot,JobLauncherCommandLineRunner line 213将先前的参数与传递给新执行的新参数合并,这导致将旧参数传递给新的执行:

return merge(nextParameters, jobParameters);

如果没有参数,似乎再也无法进行这项工作,除非我错过了一些东西。可能是春季批处理中的错误吗?

1 个答案:

答案 0 :(得分:0)

RunIdIncrementer的正常行为似乎会增加JobExecution的运行ID,并传递其余的先前JobParameters。我不会将其称为错误。

请记住,RunIdIncrementer背后的想法仅仅是更改一个标识参数,以允许再次运行作业,即使先前运行的具有相同(其他)参数的运行成功完成并且没有重启已配置。

您始终可以通过实现JobParametersIncrementer来创建自定义的增量器。

另一种替代方法是使用JobParametersBuilder来构建JobParameters对象,然后使用JobLauncher使用这些参数来运行您的作业。如果我正在运行本来具有相同的JobParameters的作业,我通常使用毫秒为单位的当前系统时间来创建唯一性。显然,您将必须弄清楚从命令行(或其他位置)提取特定参数并对其进行迭代以填充JobParameters对象的逻辑。

示例:

public JobExecution executeJob(Job job) {
    JobExecution jobExecution = null;
    try {
        JobParameters jobParameters =
            new JobParametersBuilder()
                .addLong( "time.millis", System.currentTimeMillis(), true)
                .addString( "param1", "value1", true)
                .toJobParameters();
        jobExecution = jobLauncher.run( job, jobParameters );
    } catch ( JobInstanceAlreadyCompleteException | JobRestartException | JobParametersInvalidException | JobExecutionAlreadyRunningException e ) {
        e.printStackTrace();
    }
    return jobExecution;
}