Spring 批处理 - 要读取的文件不可用
Spring Batch - File to be read is not available
我有一个 Spring 批处理作业,第一步是读取文件并写入数据库。如果文件不存在,我希望系统等待 x 分钟,然后重试该步骤,最多尝试 y 次。我怎样才能做到这一点?
详情
ItemReader
如下:
@Bean
public ItemReader<Car> reader(LineMapper<Car> lineMapper, File file) {
FlatFileItemReader<Car> flatFileItemReader = new FlatFileItemReader<Car>();
flatFileItemReader.setResource(new FileSystemResource(file));
final int NUMBER_OF_HEADER_LINES = 1;
flatFileItemReader.setLinesToSkip(NUMBER_OF_HEADER_LINES);
flatFileItemReader.setLineMapper(lineMapper);
return flatFileItemReader;
}
目前,如果file
不存在,抛出以下异常,作业失败。
2016-08-05 09:30:10.277 ERROR 7668 --- [ main] o.s.batch.core.step.AbstractStep : Encountered an error executing step stepLoadFile in job testJob
org.springframework.batch.item.ItemStreamException: Failed to initialize the reader
at org.springframework.batch.item.support.AbstractItemCountingItemStreamItemReader.open(AbstractItemCountingItemStreamItemReader.java:147) ~[spring-batch-infrastructure-3.0.6.RELEASE.jar:3.0.6.RELEASE]
at org.springframework.batch.item.support.CompositeItemStream.open(CompositeItemStream.java:96) ~[spring-batch-infrastructure-3.0.6.RELEASE.jar:3.0.6.RELEASE]
at org.springframework.batch.core.step.tasklet.TaskletStep.open(TaskletStep.java:310) ~[spring-batch-core-3.0.6.RELEASE.jar:3.0.6.RELEASE]
at org.springframework.batch.core.step.AbstractStep.execute(AbstractStep.java:197) ~[spring-batch-core-3.0.6.RELEASE.jar:3.0.6.RELEASE]
at org.springframework.batch.core.job.SimpleStepHandler.handleStep(SimpleStepHandler.java:148) [spring-batch-core-3.0.6.RELEASE.jar:3.0.6.RELEASE]
at org.springframework.batch.core.job.AbstractJob.handleStep(AbstractJob.java:392) [spring-batch-core-3.0.6.RELEASE.jar:3.0.6.RELEASE]
at org.springframework.batch.core.job.SimpleJob.doExecute(SimpleJob.java:135) [spring-batch-core-3.0.6.RELEASE.jar:3.0.6.RELEASE]
at org.springframework.batch.core.job.AbstractJob.execute(AbstractJob.java:306) [spring-batch-core-3.0.6.RELEASE.jar:3.0.6.RELEASE]
at org.springframework.batch.core.launch.support.SimpleJobLauncher.run(SimpleJobLauncher.java:135) [spring-batch-core-3.0.6.RELEASE.jar:3.0.6.RELEASE]
at org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:50) [spring-core-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.batch.core.launch.support.SimpleJobLauncher.run(SimpleJobLauncher.java:128) [spring-batch-core-3.0.6.RELEASE.jar:3.0.6.RELEASE]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_91]
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) ~[na:1.8.0_91]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) ~[na:1.8.0_91]
at java.lang.reflect.Method.invoke(Unknown Source) ~[na:1.8.0_91]
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:302) [spring-aop-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190) [spring-aop-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) [spring-aop-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration$PassthruAdvice.invoke(SimpleBatchConfiguration.java:127) [spring-batch-core-3.0.6.RELEASE.jar:3.0.6.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) [spring-aop-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:208) [spring-aop-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at com.sun.proxy.$Proxy78.run(Unknown Source) [na:na]
at org.springframework.boot.autoconfigure.batch.JobLauncherCommandLineRunner.execute(JobLauncherCommandLineRunner.java:215) [spring-boot-autoconfigure-1.3.1.RELEASE.jar:1.3.1.RELEASE]
at org.springframework.boot.autoconfigure.batch.JobLauncherCommandLineRunner.executeLocalJobs(JobLauncherCommandLineRunner.java:232) [spring-boot-autoconfigure-1.3.1.RELEASE.jar:1.3.1.RELEASE]
at org.springframework.boot.autoconfigure.batch.JobLauncherCommandLineRunner.launchJobFromProperties(JobLauncherCommandLineRunner.java:124) [spring-boot-autoconfigure-1.3.1.RELEASE.jar:1.3.1.RELEASE]
at org.springframework.boot.autoconfigure.batch.JobLauncherCommandLineRunner.run(JobLauncherCommandLineRunner.java:118) [spring-boot-autoconfigure-1.3.1.RELEASE.jar:1.3.1.RELEASE]
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:804) [spring-boot-1.3.1.RELEASE.jar:1.3.1.RELEASE]
at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:788) [spring-boot-1.3.1.RELEASE.jar:1.3.1.RELEASE]
at org.springframework.boot.SpringApplication.afterRefresh(SpringApplication.java:775) [spring-boot-1.3.1.RELEASE.jar:1.3.1.RELEASE]
at org.springframework.boot.SpringApplication.doRun(SpringApplication.java:366) [spring-boot-1.3.1.RELEASE.jar:1.3.1.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:305) [spring-boot-1.3.1.RELEASE.jar:1.3.1.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1124) [spring-boot-1.3.1.RELEASE.jar:1.3.1.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1113) [spring-boot-1.3.1.RELEASE.jar:1.3.1.RELEASE]
at com.myorg.LoadApplication.main(LoadApplication.java:15) [bin/:na]
Caused by: java.lang.IllegalStateException: Input resource must exist (reader is in 'strict' mode): file [C:\test\TestData.csv]
at org.springframework.batch.item.file.FlatFileItemReader.doOpen(FlatFileItemReader.java:251) ~[spring-batch-infrastructure-3.0.6.RELEASE.jar:3.0.6.RELEASE]
at org.springframework.batch.item.support.AbstractItemCountingItemStreamItemReader.open(AbstractItemCountingItemStreamItemReader.java:144) ~[spring-batch-infrastructure-3.0.6.RELEASE.jar:3.0.6.RELEASE]
... 33 common frames omitted
com.myorg.LoadApplication 代码启动批处理应用程序并且是:
@SpringBootApplication
public class LoadApplication {
public static void main(String[] args) {
SpringApplication.run(LoadApplication.class, args);
}
}
更新
阅读 dimitrisli 回答后,我在我的 @Configuration
class 中尝试了这个(注意:我还在配置 class 中添加了 @EnableRetry
):
@Bean
public ItemReader<Test> reader(LineMapper<Test> lineMapper, ApplicationProperties properties) {
FlatFileItemReader<Test> flatFileItemReader = new FlatFileItemReader<Test>() {
@Override
@Retryable(value = {ItemStreamException.class}, maxAttempts=5)
public void open(ExecutionContext executionContext) throws ItemStreamException {
super.open(executionContext);
}
@Override
@Retryable(maxAttempts=5)
public Holding read() throws UnexpectedInputException, ParseException, Exception {
return super.read();
}
};
flatFileItemReader.setResource(new FileSystemResource(properties.getLoadFile()));
flatFileItemReader.setLineMapper(lineMapper);
return flatFileItemReader;
}
ItemStreamException
被抛出并且应用程序退出而不重试。我怎样才能让它重试?
你应该使用 spring-retry 。
RetryTemplate
与 TimeoutRetryPolicy
相结合将允许您编写您想要的功能。
您需要将 @EnableRetry
添加到配置中 class 并将 @Retryable
添加到方法中。
是否需要先实例化ItemReader才能启动应用程序?如果不是,您应该只创建 bean 并在上下文刷新后使用另一种方法对其进行初始化。
通过使用 Spring Retry,来自 Spring Batch
的独立衍生项目:假设可能抛出异常,您希望从中恢复并重试,比如 5 次,是 ItemStreamException
那么您必须将可能抛出此异常的方法修饰为:
@Retryable(value = {ItemStreamException.class}, maxAttempts = 5)
public void myMethod() {
//..
}
我有一个 Spring 批处理作业,第一步是读取文件并写入数据库。如果文件不存在,我希望系统等待 x 分钟,然后重试该步骤,最多尝试 y 次。我怎样才能做到这一点?
详情
ItemReader
如下:
@Bean
public ItemReader<Car> reader(LineMapper<Car> lineMapper, File file) {
FlatFileItemReader<Car> flatFileItemReader = new FlatFileItemReader<Car>();
flatFileItemReader.setResource(new FileSystemResource(file));
final int NUMBER_OF_HEADER_LINES = 1;
flatFileItemReader.setLinesToSkip(NUMBER_OF_HEADER_LINES);
flatFileItemReader.setLineMapper(lineMapper);
return flatFileItemReader;
}
目前,如果file
不存在,抛出以下异常,作业失败。
2016-08-05 09:30:10.277 ERROR 7668 --- [ main] o.s.batch.core.step.AbstractStep : Encountered an error executing step stepLoadFile in job testJob
org.springframework.batch.item.ItemStreamException: Failed to initialize the reader
at org.springframework.batch.item.support.AbstractItemCountingItemStreamItemReader.open(AbstractItemCountingItemStreamItemReader.java:147) ~[spring-batch-infrastructure-3.0.6.RELEASE.jar:3.0.6.RELEASE]
at org.springframework.batch.item.support.CompositeItemStream.open(CompositeItemStream.java:96) ~[spring-batch-infrastructure-3.0.6.RELEASE.jar:3.0.6.RELEASE]
at org.springframework.batch.core.step.tasklet.TaskletStep.open(TaskletStep.java:310) ~[spring-batch-core-3.0.6.RELEASE.jar:3.0.6.RELEASE]
at org.springframework.batch.core.step.AbstractStep.execute(AbstractStep.java:197) ~[spring-batch-core-3.0.6.RELEASE.jar:3.0.6.RELEASE]
at org.springframework.batch.core.job.SimpleStepHandler.handleStep(SimpleStepHandler.java:148) [spring-batch-core-3.0.6.RELEASE.jar:3.0.6.RELEASE]
at org.springframework.batch.core.job.AbstractJob.handleStep(AbstractJob.java:392) [spring-batch-core-3.0.6.RELEASE.jar:3.0.6.RELEASE]
at org.springframework.batch.core.job.SimpleJob.doExecute(SimpleJob.java:135) [spring-batch-core-3.0.6.RELEASE.jar:3.0.6.RELEASE]
at org.springframework.batch.core.job.AbstractJob.execute(AbstractJob.java:306) [spring-batch-core-3.0.6.RELEASE.jar:3.0.6.RELEASE]
at org.springframework.batch.core.launch.support.SimpleJobLauncher.run(SimpleJobLauncher.java:135) [spring-batch-core-3.0.6.RELEASE.jar:3.0.6.RELEASE]
at org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:50) [spring-core-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.batch.core.launch.support.SimpleJobLauncher.run(SimpleJobLauncher.java:128) [spring-batch-core-3.0.6.RELEASE.jar:3.0.6.RELEASE]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_91]
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) ~[na:1.8.0_91]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) ~[na:1.8.0_91]
at java.lang.reflect.Method.invoke(Unknown Source) ~[na:1.8.0_91]
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:302) [spring-aop-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190) [spring-aop-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) [spring-aop-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration$PassthruAdvice.invoke(SimpleBatchConfiguration.java:127) [spring-batch-core-3.0.6.RELEASE.jar:3.0.6.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) [spring-aop-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:208) [spring-aop-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at com.sun.proxy.$Proxy78.run(Unknown Source) [na:na]
at org.springframework.boot.autoconfigure.batch.JobLauncherCommandLineRunner.execute(JobLauncherCommandLineRunner.java:215) [spring-boot-autoconfigure-1.3.1.RELEASE.jar:1.3.1.RELEASE]
at org.springframework.boot.autoconfigure.batch.JobLauncherCommandLineRunner.executeLocalJobs(JobLauncherCommandLineRunner.java:232) [spring-boot-autoconfigure-1.3.1.RELEASE.jar:1.3.1.RELEASE]
at org.springframework.boot.autoconfigure.batch.JobLauncherCommandLineRunner.launchJobFromProperties(JobLauncherCommandLineRunner.java:124) [spring-boot-autoconfigure-1.3.1.RELEASE.jar:1.3.1.RELEASE]
at org.springframework.boot.autoconfigure.batch.JobLauncherCommandLineRunner.run(JobLauncherCommandLineRunner.java:118) [spring-boot-autoconfigure-1.3.1.RELEASE.jar:1.3.1.RELEASE]
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:804) [spring-boot-1.3.1.RELEASE.jar:1.3.1.RELEASE]
at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:788) [spring-boot-1.3.1.RELEASE.jar:1.3.1.RELEASE]
at org.springframework.boot.SpringApplication.afterRefresh(SpringApplication.java:775) [spring-boot-1.3.1.RELEASE.jar:1.3.1.RELEASE]
at org.springframework.boot.SpringApplication.doRun(SpringApplication.java:366) [spring-boot-1.3.1.RELEASE.jar:1.3.1.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:305) [spring-boot-1.3.1.RELEASE.jar:1.3.1.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1124) [spring-boot-1.3.1.RELEASE.jar:1.3.1.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1113) [spring-boot-1.3.1.RELEASE.jar:1.3.1.RELEASE]
at com.myorg.LoadApplication.main(LoadApplication.java:15) [bin/:na]
Caused by: java.lang.IllegalStateException: Input resource must exist (reader is in 'strict' mode): file [C:\test\TestData.csv]
at org.springframework.batch.item.file.FlatFileItemReader.doOpen(FlatFileItemReader.java:251) ~[spring-batch-infrastructure-3.0.6.RELEASE.jar:3.0.6.RELEASE]
at org.springframework.batch.item.support.AbstractItemCountingItemStreamItemReader.open(AbstractItemCountingItemStreamItemReader.java:144) ~[spring-batch-infrastructure-3.0.6.RELEASE.jar:3.0.6.RELEASE]
... 33 common frames omitted
com.myorg.LoadApplication 代码启动批处理应用程序并且是:
@SpringBootApplication
public class LoadApplication {
public static void main(String[] args) {
SpringApplication.run(LoadApplication.class, args);
}
}
更新
阅读 dimitrisli 回答后,我在我的 @Configuration
class 中尝试了这个(注意:我还在配置 class 中添加了 @EnableRetry
):
@Bean
public ItemReader<Test> reader(LineMapper<Test> lineMapper, ApplicationProperties properties) {
FlatFileItemReader<Test> flatFileItemReader = new FlatFileItemReader<Test>() {
@Override
@Retryable(value = {ItemStreamException.class}, maxAttempts=5)
public void open(ExecutionContext executionContext) throws ItemStreamException {
super.open(executionContext);
}
@Override
@Retryable(maxAttempts=5)
public Holding read() throws UnexpectedInputException, ParseException, Exception {
return super.read();
}
};
flatFileItemReader.setResource(new FileSystemResource(properties.getLoadFile()));
flatFileItemReader.setLineMapper(lineMapper);
return flatFileItemReader;
}
ItemStreamException
被抛出并且应用程序退出而不重试。我怎样才能让它重试?
你应该使用 spring-retry 。
RetryTemplate
与 TimeoutRetryPolicy
相结合将允许您编写您想要的功能。
您需要将 @EnableRetry
添加到配置中 class 并将 @Retryable
添加到方法中。
是否需要先实例化ItemReader才能启动应用程序?如果不是,您应该只创建 bean 并在上下文刷新后使用另一种方法对其进行初始化。
通过使用 Spring Retry,来自 Spring Batch
的独立衍生项目:假设可能抛出异常,您希望从中恢复并重试,比如 5 次,是 ItemStreamException
那么您必须将可能抛出此异常的方法修饰为:
@Retryable(value = {ItemStreamException.class}, maxAttempts = 5)
public void myMethod() {
//..
}