[英]Partitioned Job can't stop by itself after finishing? Spring Batch
我写了一个两步作业,其中两步之一是分区步骤。 分区步骤使用 TaskExecutorPartitionHandler 并在线程中运行 5 个从步骤。 作业在 main() 方法中启动。 但它并没有在每个从属 ItemReader 返回 null 后停止 - 完成符号。 即使在程序运行通过 main() 方法(即 System.out.println("Finished"))中的最后一行代码之后,程序进程也不会停止,挂在内存中什么也不做。 我必须按下 Eclipse 面板上的停止按钮才能停止程序。
以下是 JobLauncher.run() 返回的 JobExecution 的内容,表示 Job 运行的成功状态。
JobExecution: id=0, version=2, startTime=Fri Nov 27 06:05:23 CST 2015, endTime=Fri Nov 27 06:05:39 CST 2015, lastUpdated=Fri Nov 27 06:05:39 CST 2015, status =COMPLETED, exitStatus=exitCode=COMPLETED;exitDescription=, job=[JobInstance: id=0, version=0, Job=[jobCensoredPages]], jobParameters=[{}]
7217
完成的
为什么运行成功的 Spring Batch 程序仍然挂起? 请指点我在哪里解决。 我怀疑 Spring Batch 管理的多线程部分不会停止..
简单的作业运行代码
Job job = (Job) context.getBean("jobPages");
try {
JobParameters p=new JobParametersBuilder()
.toJobParameters();
JobExecution result = launcher.run(job, new JobParameters());
System.out.println(result.toString());
} catch (Exception e) {
e.printStackTrace();
}
context.getBean("idSet");
AtomicInteger n=(AtomicInteger) context.getBean("pageCount");
System.out.println(n.get());
System.out.println("Finished");
Patitioner 和 PatitionHandler 的配置
@Bean @Autowired
public PartitionHandler beanPartitionHandler(
TaskExecutor beanTaskExecutor,
@Qualifier("beanStepSlave") Step beanStepSlave
) throws Exception
{
TaskExecutorPartitionHandler h=new TaskExecutorPartitionHandler();
h.setGridSize(5);
h.setTaskExecutor(beanTaskExecutor);
h.setStep(beanStepSlave);
h.afterPropertiesSet();
return h;
}
@Bean public TaskExecutor beanTaskExecutor() {
ThreadPoolTaskExecutor e = new ThreadPoolTaskExecutor();
e.setMaxPoolSize(5);
e.setCorePoolSize(5);
e.afterPropertiesSet();
return e;
}
唯一的一步,它是奴隶的一步
@Bean public Step beanStepMaster(
Step beanStepSlave,
Partitioner beanPartitioner,
PartitionHandler beanPartitionHandler
) throws Exception
{
return stepBuilderFactory().get("stepMaster")
.partitioner(beanStepSlave)
.partitioner("stepSlave", beanPartitioner)
.partitionHandler(partitionHandler)
.build();
}
@Bean @Autowired
public Step beanStepSlave(
ItemReader<String> beanReaderTest,
ItemProcessor<String, String> beanProcessorTest,
ItemWriter<String> beanWriterTest) throws Exception{
return stepBuilderFactory().get("stepSlave")
.<String, String>chunk(1)
.reader(beanReaderTest)
.processor(beanProcessorTest)
.writer(beanWriterTest)
.build();
}
我的 pom.xml 文件
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>4.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.batch</groupId>
<artifactId>spring-batch-core</artifactId>
<version>RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<version>1.1.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>RELEASE</version>
</dependency>
当我使用 ThreadPoolTaskExecutor 时,我的分区 Spring 批处理应用程序在完成时也遇到了困难。 此外,我看到执行程序不允许所有分区的工作完成。
我找到了两种解决这些问题的方法。
第一个解决方案是使用 SimpleAsyncTaskExecutor 而不是 ThreadPoolTaskExecutor。 如果您不介意重新创建线程的额外开销,这是一个简单的解决方法。
第二种解决方案是创建一个 JobExecutionListener 来调用 ThreadPoolTaskExecutor 上的关闭。
我创建了一个这样的 JobExecutionListener:
@Bean
public JobExecutionListener jobExecutionListener(ThreadPoolTaskExecutor executor) {
return new JobExecutionListener() {
private ThreadPoolTaskExecutor taskExecutor = executor;
@Override
public void beforeJob(JobExecution jobExecution) {
}
@Override
public void afterJob(JobExecution jobExecution) {
taskExecutor.shutdown();
}
};
}
并将其添加到我的工作定义中,如下所示:
@Bean
public Job partitionedJob(){
return jobBuilders.get("partitionedJob")
.listener(jobExecutionListener(taskExecutor()))
.start(partitionedStep())
.build();
}
以上所有答案都是黑客/解决方法。 问题中发布的问题的根本原因是 threadPoolTaskExecutor 不共享步骤的生命周期。 因此,在销毁 step/job context 时,线程池不会自动销毁并且永远运行。 将 threadPoolExecutor 放在 stepContext "@StepScope" 中应该可以解决问题。 Spring 负责销毁它。
@Bean @StepScope public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
您的问题有两种解决方案,尽管我不知道原因。
首先,您可以使用CommandLineJobRunner
来启动Job
。 请参阅此处的文档。 此类在作业结束时自动退出程序,并将 ExitStatus 转换为返回码( COMPLETED
= 0, FAILED
= 1...)。 默认返回码由SimpleJvmExitCodeMapper
提供。
第二种解决方案是在JobLauncher.run()
之后手动调用System.exit()
指令。 您还可以手动转换Job
的ExitStatus
并在手动退出时使用它:
// Create Job
JobLauncher jobLauncher = (JobLauncher) context.getBean("jobLauncher");
Job job = (Job) context.getBean(jobName);
// Create return codes mapper
SimpleJvmExitCodeMapper mapper = new SimpleJvmExitCodeMapper();
// Start Job
JobExecution execution = jobLauncher.run(job, new JobParameters());
// Close context
context.close();
// Map codes and exit
String status = execution.getExitStatus().getExitCode();
Integer returnCode = mapper.intValue(status);
System.exit(returnCode);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.