我一直遵循spring batch doc,但无法使我的工作异步运行。
因此,我正在从Web容器运行作业,该作业将通过REST端点触发。
我想获得JobInstance ID 作为响应传递它,然后再完成整个作业。因此,他们以后可以使用JobInstance ID来检查作业的状态,而不必等待。但是我无法正常工作。以下是我尝试的示例代码。请让我知道我在想什么或错了。
BatchConfig以创建异步JobLauncher
@Configuration
public class BatchConfig {
@Autowired
JobRepository jobRepository;
@Bean
public JobLauncher simpleJobLauncher() throws Exception {
SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
jobLauncher.setJobRepository(jobRepository);
jobLauncher.setTaskExecutor(new SimpleAsyncTaskExecutor());
jobLauncher.afterPropertiesSet();
return jobLauncher;
}
}
控制器
@Autowired
JobLauncher jobLauncher;
@RequestMapping(value="/trigger-job", method = RequestMethod.GET)
public Long workHard() throws Exception {
JobParameters jobParameters = new JobParametersBuilder().
addLong("time", System.currentTimeMillis())
.toJobParameters();
JobExecution jobExecution = jobLauncher.run(batchComponent.customJob("paramhere"), jobParameters);
System.out.println(jobExecution.getJobInstance().getInstanceId());
System.out.println("OK RESPONSE");
return jobExecution.getJobInstance().getInstanceId();
}
将JobBuilder作为组件
@Component
public class BatchComponent {
@Autowired
private JobBuilderFactory jobBuilderFactory;
@Autowired
private StepBuilderFactory stepBuilderFactory;
public Job customJob(String someParam) throws Exception {
return jobBuilderFactory.get("personProcessor")
.incrementer(new RunIdIncrementer()).listener(listener())
.flow(personPorcessStep(someParam)).end().build();
}
private Step personPorcessStep(String someParam) throws Exception {
return stepBuilderFactory.get("personProcessStep").<PersonInput, PersonOutput>chunk(1)
.reader(new PersonReader(someParam)).faultTolerant().
skipPolicy(new DataDuplicateSkipper()).processor(new PersonProcessor())
.writer(new PersonWriter()).build();
}
private JobExecutionListener listener() {
return new PersonJobCompletionListener();
}
private class PersonInput {
String firstName;
public PersonInput(String firstName) {
this.firstName = firstName;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
}
private class PersonOutput {
String firstName;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
}
public class PersonReader implements ItemReader<PersonInput> {
private List<PersonInput> items;
private int count = 0;
public PersonReader(String someParam) throws InterruptedException {
Thread.sleep(10000L); //to simulate processing
//manipulate and provide data in the read method
//just for testing i have given some dummy example
items = new ArrayList<PersonInput>();
PersonInput pi = new PersonInput("john");
items.add(pi);
}
@Override
public PersonInput read() {
if (count < items.size()) {
return items.get(count++);
}
return null;
}
}
public class DataDuplicateSkipper implements SkipPolicy {
@Override
public boolean shouldSkip(Throwable exception, int skipCount) throws SkipLimitExceededException {
if (exception instanceof DataIntegrityViolationException) {
return true;
}
return true;
}
}
private class PersonProcessor implements ItemProcessor<PersonInput, PersonOutput> {
@Override
public PersonOutput process(PersonInput item) throws Exception {
return null;
}
}
private class PersonWriter implements org.springframework.batch.item.ItemWriter<PersonOutput> {
@Override
public void write(List<? extends PersonOutput> results) throws Exception {
return;
}
}
private class PersonJobCompletionListener implements JobExecutionListener {
public PersonJobCompletionListener() {
}
@Override
public void beforeJob(JobExecution jobExecution) {
}
@Override
public void afterJob(JobExecution jobExecution) {
System.out.println("JOB COMPLETED");
}
}
}
主要功能
@SpringBootApplication
@EnableBatchProcessing
@EnableScheduling
@EnableAsync
public class SpringBatchTestApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBatchTestApplication.class, args);
}
}
我正在使用基于注释的配置,并在下面的批处理软件包中使用gradle。
compile('org.springframework.boot:spring-boot-starter-batch')
请让我知道是否需要更多信息。我找不到运行此常见用例的任何示例。
感谢您的时间。
答案 0 :(得分:1)
根据spring文档以异步返回http请求的响应,需要使用org.springframework.core.task.SimpleAsyncTaskExecutor。
spring TaskExecutor接口的任何实现都可以用来控制作业的异步执行方式。
<bean id="jobLauncher"
class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
<property name="jobRepository" ref="jobRepository" />
<property name="taskExecutor">
<bean class="org.springframework.core.task.SimpleAsyncTaskExecutor" />
</property>
答案 1 :(得分:0)
JobExecution jobExecution = jobLauncher.run(batchComponent.customJob("paramhere"), jobParameters);
。 Joblauncher将在Job完成后等待,然后返回任何内容,这就是如果您的问题,您的服务可能需要很长时间才能响应的原因。
如果需要异步功能,则可能需要查看Spring的@EnableAsync
和@Async
。
答案 2 :(得分:0)
尽管您具有自定义jobLauncher
,但是您正在使用Spring提供的默认jobLauncher
来运行作业。您可以在控制器中自动接线simpleJobLauncher
并尝试一下吗?
答案 3 :(得分:0)
尝试此操作,在您的配置中,您需要使用 @Bean(name =“ myJobLauncher”)和 SimpleAsyncTaskExecutor 创建customJobLauncher,并且将使用 @控制器中的限定符。
@Bean(name = "myJobLauncher")
public JobLauncher simpleJobLauncher() throws Exception {
SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
jobLauncher.setJobRepository(jobRepository);
jobLauncher.setTaskExecutor(new SimpleAsyncTaskExecutor());
jobLauncher.afterPropertiesSet();
return jobLauncher;
}
在您的控制器中
@Autowired
@Qualifier("myJobLauncher")
private JobLauncher jobLauncher;
答案 4 :(得分:0)
如果我查看您的代码,就会发现一些错误。 首先,您的自定义配置未加载,因为如果已加载,则对于同一接口的重复bean实例,注入将失败。
春季靴子有很多魔力,但是如果您不告诉他进行组件扫描,则不会加载任何东西。
我可以看到的第二个问题是您的BatchConfig类:它没有扩展DefaultBatchConfigure,也没有覆盖getJobLauncher(),因此即使启动魔术将加载所有内容,您也将获得默认的类。 这是一个有效的配置,并且符合文档@EnableBatchProcessing API
BatchConfig
useLayoutEffect
主要功能
parameters
答案 5 :(得分:0)
我知道这是一个老问题,但是无论如何我都会将此答案发布给以后的用户。
在检查了代码之后,我无法告诉您为什么会有这个问题,但是我建议您使用Qualifier注释,再像这样使用ThreadPoolTaskExecutor,看看它是否可以解决您的问题。
您还可以查看本教程:Asynchronous Spring Batch Job Processing了解更多详细信息。这将帮助您异步配置Spring Batch作业。本教程是我写的。
@Configuration
public class BatchConfig {
@Autowired
private JobRepository jobRepository;
@Bean
public TaskExecutor threadPoolTaskExecutor(){
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setMaxPoolSize(12);
executor.setCorePoolSize(8);
executor.setQueueCapacity(15);
return executor;
}
@Bean
public JobLauncher asyncJobLauncher() throws Exception {
SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
jobLauncher.setJobRepository(jobRepository);
jobLauncher.setTaskExecutor(threadPoolTaskExecutor());
return jobLauncher;
}
}
答案 6 :(得分:0)
如果您使用的是 Lombok,这可能对您有所帮助:
TLDR: Lombok @AllArgsConstructor
似乎不适用于 @Qualifier
注释
编辑:如果您在 @Qualifier
文件中启用了 lombok.config
注释,以便能够像这样将 @Qualifier
与 @AllArgsConstructor
一起使用:
lombok.copyableAnnotations += org.springframework.beans.factory.annotation.Qualifier
我知道老问题,但是我遇到了完全相同的问题,但没有一个答案能解决它。
我像这样配置了异步作业启动器并添加了限定符以确保注入此 jobLauncher:
@Bean(name = "asyncJobLauncher")
public JobLauncher simpleJobLauncher(JobRepository jobRepository) throws Exception {
SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
jobLauncher.setJobRepository(jobRepository);
jobLauncher.setTaskExecutor(new SimpleAsyncTaskExecutor());
jobLauncher.afterPropertiesSet();
return jobLauncher;
}
然后像这样注入
@Qualifier("asyncJobLauncher")
private final JobLauncher jobLauncher;
我在将 Lombok @AllArgsConstructor
更改为自动装配后使用它,注入了正确的作业启动器,现在该作业异步执行:
@Autowired
@Qualifier("asyncJobLauncher")
private JobLauncher jobLauncher;
我也不必从 DefaultBatchConfigurer