#spring-boot #spring-batch
#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=значение1 myparam2= значение2
Поскольку задание использует 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 в приложение.
Есть ли способ удалить параметр задания и не передавать его следующему экземпляру?
Код приложения:
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 batch и spring boot, и вот что происходит. Строка 273 JobParametersBuilder добавляет параметры из последнего предыдущего экземпляра задания в карту nextParameters вместе с любыми параметрами, добавленными JobParametersIncrementer:
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, строка 213 JobLauncherCommandLineRunner объединяет предыдущие параметры с новыми параметрами, переданными для нового выполнения, что приводит к передаче старого параметра в новое выполнение:
return merge(nextParameters, jobParameters);
Кажется, что невозможно снова запустить задание без параметра, если я чего-то не упустил. Может ли это быть ошибкой в spring batch?
Комментарии:
1. Лучше вставить фрагмент кода внутри текста вопроса, используя функции редактора для отображения кода или текста и так далее. Четко задайте в заголовке вопроса, в чем заключается ваша проблема.
2. Спасибо за предложение. Изменил его.
3.
But the job parameter myparam from the first instance is carried forward to the new job instance
: Можете ли вы показать, как вы запускаете задание в первый и второй раз? Пожалуйста, поделитесь либо командой, либо кодом, который вы используете для запуска задания в обоих случаях.4. Теперь включены явные команды и код приложения
Ответ №1:
Нормальное поведение для RunIdIncrementer
, по-видимому, увеличивает идентификатор запуска для JobExecution
и передает оставшийся предыдущий 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;
}
Комментарии:
1. Спасибо за ответ, @PhilipWrage. Я попробую пользовательский JobParametersIncrementer, который возвращает только идентификатор запуска, а не предыдущие параметры.