Как запустить Spring Batch Jobs в определенном порядке (Spring Boot)?
Я разрабатываю с помощью Spring Batch, используя Spring Boot.
Я с минимальной конфигурацией, предоставляемой Spring Boot, и определил несколько заданий (вообще без конфигурации XML). Но когда я запускаю приложение,
SpringApplication.run(App.class, args);
задания последовательно выполняются в произвольном порядке.
Я определяю работу таким образом в @Configuration
аннотированные классы, Spring делает все остальное:
@Bean
public Job requestTickets() {
return jobBuilderFactory.get(Config.JOB_REQUEST_TICKETS)
.start(stepRequestTickets())
.build();
}
Как я могу поручить каркасу запускать задания в определенном порядке?
РЕДАКТИРОВАТЬ: Может ли это предупреждение дать подсказку? (Может быть нечего)
2016-12-29 17:45:33.320 WARN 3528 --- [main] o.s.b.c.c.a.DefaultBatchConfigurer: No datasource was provided...using a Map based JobRepository
5 ответов
1.Вы сначала отключите автоматический запуск задания, указав spring.batch.job.enabled=false
в приложении. свойства
2. В вашем основном классе, сделать - ApplicationContext ctx = SpringApplication.run(SpringBatchMain.class, args);
при условии, что ваш главный класс назван - SpringBatchMain.java.
Это будет инициализировать контекст без запуска каких-либо заданий.
3. Как только контекст инициализирован, вы можете сделать: JobLauncher jobLauncher = (JobLauncher) ctx.getBean("jobLauncher");
или сделать Autowired
для этого bean-компонента JobLauncher в главном классе и запускать определенные задания последовательно в определенном последовательном порядке, вызывая jobLauncher.run(job, jobParameters)
,
Вы можете получить конкретные job
экземпляры из контекста, инициализированные на шаге № 2.
Вы всегда можете использовать любую упорядоченную коллекцию для размещения своих заданий и запуска заданий, просматривая эту коллекцию.
4. Этот метод работает до тех пор, пока ваш JobLauncher настроен на синхронность, т.е. основной поток ожидает jobLauncher.run()
вызов завершить, и это поведение по умолчанию для jobLauncher.
Если вы определили свой jobLauncher для использования AsyncTaskExecutor, тогда задания будут запускаться параллельно, и последовательное упорядочение не будет поддерживаться.
Надеюсь, поможет!!
РЕДАКТИРОВАТЬ:
Я экспериментировал с @Order
аннотация, как указал Стефан Николл, и кажется, что она помогает только в создании упорядоченной коллекции заданий и что вы можете выполнять итерации и запускать задания в указанном порядке.
Этот ниже компонент дает мне работу в указанном порядке,
@Component
public class MyJobs {
@Autowired
private List<Job> jobs;
public List<Job> getJobs() {
return jobs;
}
}
и я могу сделать, MyJobs myJobs = (MyJobs) ctx.getBean("myJobs");
в основном классе определяется боб,
@Bean
public MyJobs myJobs() {
return new MyJobs();
}
Я могу перебрать myJobs
и запускайте задания в том порядке, который указан аннотацией @Order.
Order them.
@Bean
@Order(42)
public Job requestTickets() {
return jobBuilderFactory.get(Config.JOB_REQUEST_TICKETS)
.start(stepRequestTickets())
.build();
}
See the javadoc of@Order
Больше подробностей.
Вот иллюстрация решения.
Это так странно, похоже, что мы взломали процесс.
spring.batch.job.enabled = ложь
@SpringBootApplication
@EnableBatchProcessing
public class MyApplication {
public static void main(String[] args)
throws JobParametersInvalidException, JobExecutionAlreadyRunningException, JobRestartException, JobInstanceAlreadyCompleteException {
ConfigurableApplicationContext ctx = SpringApplication.run(MyApplication.class, args);
JobLauncher jobLauncher = (JobLauncher) ctx.getBean("jobLauncher");
Job job1= (Job) ctx.getBean("job1");
Job job2= (Job) ctx.getBean("job2");
jobLauncher.run(job1,new JobParameters());
jobLauncher.run(job2,new JobParameters());
}
}
У меня недостаточно представителей, чтобы комментировать. Но пытались ли вы просто запустить свои работы в нужном вам порядке?
Вам нужно установить spring.batch.job.enabled=false в ваш application.properties, чтобы ваши задания не запускались автоматически.
Тогда просто используйте панель запуска, чтобы запускать ваши задания в нужном вам порядке.
@RunWith(SpringRunner.class)
@SpringBootTest(classes = { TestConfiguration.class, TestDataSourceConfiguration.class, TestBatchConfig.class })
public class JobOrderTest {
@Autowired
JobLauncher jobLauncher;
@Mock
Job firstJob;
@Mock
Job secondJob;
@Mock
Job thirdJob;
@Mock
JobParametersValidator jobParametersValidator;
@Test
public void jobInOrderTest() throws JobExecutionAlreadyRunningException, JobRestartException, JobInstanceAlreadyCompleteException, JobParametersInvalidException {
when(firstJob.getName()).thenReturn(UUID.randomUUID().toString());
when(secondJob.getName()).thenReturn(UUID.randomUUID().toString());
when(thirdJob.getName()).thenReturn(UUID.randomUUID().toString());
when(firstJob.getJobParametersValidator()).thenReturn(jobParametersValidator);
when(secondJob.getJobParametersValidator()).thenReturn(jobParametersValidator);
when(thirdJob.getJobParametersValidator()).thenReturn(jobParametersValidator);
jobLauncher.run(firstJob, new JobParameters());
jobLauncher.run(secondJob, new JobParameters());
jobLauncher.run(thirdJob, new JobParameters());
}
}
Вот вывод
2016-12-30 09:48:36.457 INFO 144860 --- [cTaskExecutor-1] o.s.b.c.l.support.SimpleJobLauncher : Job: [firstJob] launched with the following parameters: ...
2016-12-30 09:48:36.457 INFO 144860 --- [cTaskExecutor-1] o.s.b.c.l.support.SimpleJobLauncher : Job: [firstJob] completed with the following parameters: ...
2016-12-30 09:48:36.478 INFO 144860 --- [cTaskExecutor-2] o.s.b.c.l.support.SimpleJobLauncher : Job: [secondJob] launched with the following parameters: ...
2016-12-30 09:48:36.478 INFO 144860 --- [cTaskExecutor-2] o.s.b.c.l.support.SimpleJobLauncher : Job: [secondJob] completed with the following parameters: ...
2016-12-30 09:48:36.508 INFO 144860 --- [cTaskExecutor-3] o.s.b.c.l.support.SimpleJobLauncher : Job: [thirdJob] launched with the following parameters: ...
2016-12-30 09:48:36.508 INFO 144860 --- [cTaskExecutor-3] o.s.b.c.l.support.SimpleJobLauncher : Job: [thirdJob] completed with the following parameters: ...
Если ваша одна работа зависит от второй и так далее, то сделайте что-то подобное.
@Configuration
@EnableBatchProcessing
@Import(DataSourceConfiguration.class)
public class AppConfig {
@Autowired
private JobBuilderFactory jobs;
@Autowired
private StepBuilderFactory steps;
@Bean
public Job job(@Qualifier("step1") Step step1, @Qualifier("step2") Step step2) {
return jobs.get("myJob").start(step1).next(step2).build();
}
@Bean
protected Step step1(ItemReader<Person> reader, ItemProcessor<Person, Person> processor, ItemWriter<Person> writer) {
return steps.get("step1")
.<Person, Person> chunk(10)
.reader(reader)
.processor(processor)
.writer(writer)
.build();
}
@Bean
protected Step step2(Tasklet tasklet) {
return steps.get("step2")
.tasklet(tasklet)
.build();
}
}