如何按特定顺序运行 Spring Batch Jobs(Spring Boot)?
Posted
技术标签:
【中文标题】如何按特定顺序运行 Spring Batch Jobs(Spring Boot)?【英文标题】:How to run Spring Batch Jobs in certain order (Spring Boot)? 【发布时间】:2017-05-12 20:45:30 【问题描述】:我正在使用 Spring Boot 使用 Spring Batch 进行开发。
我使用 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
【问题讨论】:
所以你有一份工作有多个步骤或多个工作?好像,你只有一份工作。当您说 jobs 时,您是指 steps 吗?显示stepRequestTickets()
的代码。
我有多个工作。我想按固定顺序依次运行作业,但找不到订购作业的方法。这只是我如何定义 Job 的一个 sn-p。
【参考方案1】:
1.您首先通过在 application.properties
中指定spring.batch.job.enabled=false
来禁用自动作业启动
2.在你的主类中,做 - ApplicationContext ctx = SpringApplication.run(SpringBatchMain.class, args);
假设你的主类被命名为 - SpringBatchMain.java。
这将在不启动任何作业的情况下初始化上下文。
3.一旦上下文被初始化,你可以做 - JobLauncher jobLauncher = (JobLauncher) ctx.getBean("jobLauncher");
或在主类中为这个 JobLauncher bean 做 Autowired
并通过调用 jobLauncher.run(job, jobParameters)
以特定顺序依次启动特定作业。
您可以从第 2 步初始化的上下文中获取特定的 job
实例。
您始终可以使用任何有序集合将您的作业放在那里,并通过迭代该集合来启动作业。
4. 只要您的 JobLauncher 配置为同步,即主线程等待 jobLauncher.run()
调用完成,并且这是 jobLauncher 的默认行为,上述技术就可以工作。
如果您已将 jobLauncher 定义为使用 AsyncTaskExecutor,则作业将并行启动,并且不会保持顺序排序。
希望对你有帮助!!
编辑:
我正在试验 Stephane Nicoll 指出的 @Order
注释,它似乎只有助于创建有序的作业集合,并且您可以按该顺序迭代和启动作业。
这个下面的组件给了我指定顺序的工作,
@Component
public class MyJobs
@Autowired
private List<Job> jobs;
public List<Job> getJobs()
return jobs;
我可以做到,MyJobs myJobs = (MyJobs) ctx.getBean("myJobs");
在主类中提供 bean 已定义,
@Bean
public MyJobs myJobs()
return new MyJobs();
我可以遍历myJobs
并按照@Order 注释指定的顺序启动作业。
【讨论】:
成功了!但是一个额外的问题:您将@Bean
code 放在哪里?如果我将它放在main
方法中,它工作正常。但是如果我把它放在 @Component
里面,我会得到一个 Spring 错误:BeanDefinitionStoreException: Invalid bean definition with name 'myJobs' defined in class path resource [.../MyJobs.class]: factory-bean reference points back to the same bean definition
我发现@Bean
的定义是不必要的。我猜@Component
已经创建了所需的 bean。
我的意思是,如果您的工作太多且难以管理,则需要使用 Order。对于一两个作业,您可以简单地手动启动作业,而无需使用订单注释。
我使用@Order
管理它并遍历作业列表以运行作业。但我不需要声明@Bean myJobs()
代码。我可以在没有声明 @Bean 的情况下制作 ctx.getBean(MyJobs.class).getJobs()
,只需使用 @Component
部分。谢谢让它工作:)【参考方案2】:
订购它们。
@Bean
@Order(42)
public Job requestTickets()
return jobBuilderFactory.get(Config.JOB_REQUEST_TICKETS)
.start(stepRequestTickets())
.build();
有关详细信息,请参阅javadoc of @Order
。
【讨论】:
@Order
没有成功。我完全按照您的建议将注释插入了 @Order(1)
、@Order(2)
、@Order(3)
到我的 3 个作业中,但它们的执行方式与以前一样。
那么您没有使用作业启动器。或者您使用的是非常旧的 Spring Boot 版本。
@StephaneNicoll:那你是什么意思,那么你没有使用作业启动器。 jobLauncher.run(job, jobParameters)
运行特定的作业,所以它就像手动订购一样。我也测试了@Order,但它没有按照您的回答中描述的那样工作。我正在使用 Spring Boot 1.4.0。不知道 OP 有没有更新?
对我来说,无论 Job
bean 首先在 @Configuration
类中定义,无论 Order
是否指定,都会首先执行,【参考方案3】:
这是解决方案的说明。
这太奇怪了,看起来我们正在破解进程。
spring.batch.job.enabled=false
@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());
【讨论】:
【参考方案4】:我没有足够的代表发表评论。但是您是否尝试过按照您想要的顺序手动启动您的工作?
您需要在 application.properties 中设置 spring.batch.job.enabled=false,以便您的作业不会自动运行。
然后只需使用启动器按您想要的顺序启动您的作业。
@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: ...
【讨论】:
【参考方案5】:如果您的一项工作依赖于第二项工作,依此类推,请执行以下操作。
@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();
【讨论】:
我认为您在谈论订购步骤,但我需要按特定顺序运行 JOBS,因为Job_n
取决于 Job_n-1
。以上是关于如何按特定顺序运行 Spring Batch Jobs(Spring Boot)?的主要内容,如果未能解决你的问题,请参考以下文章