重新启动由命令行启动的失败的Spring(启动)批处理作业

时间:2019-12-05 15:39:50

标签: spring-boot spring-batch resume

我知道有关此主题的问题和帖子很多(除了自己的Spring文档之外),但是我承认我仍然没有设法弄清工作重启的工作方式。

首先,我正在使用Spring Boot创建我的批处理程序。我的pom.xml的相关部分如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.1.RELEASE</version>
        <relativePath /> <!-- lookup parent from repository -->
    </parent>

    ...

    <dependencies>
        <!-- Spring -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-batch</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.batch</groupId>
            <artifactId>spring-batch-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!-- Databases -->
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
        </dependency>

        <!-- Misc -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>23.0</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

主类使用@EnableBatchProcessing注释:

@SpringBootApplication
@EnableBatchProcessing
public class Migrator {

    public static void main( String[] args ) {
        SpringApplication.run( Migrator.class, args );
    }

}

正确地说,作业的配置如下:


@Configuration
public class EnigmaPartitionedJobConfig {

    @Autowired
    private JobBuilderFactory jobBuilderFactory;

    ...

    @Bean
    public Job configureJob( @Qualifier( "constraintsTurnOffStep" ) Step constraintsTurnOffStep, 
                             @Qualifier( "constraintsTurnOnStep" ) Step constraintsTurnOnStep,
                             @Qualifier( "partitionerStep" ) Step partitionerStep ) {
        return jobBuilderFactory
                        .get( "migratorPartitionedJob" )
                        .incrementer( new RunIdIncrementer() )
                        .start( constraintsTurnOffStep )
                        .next( partitionerStep )
                        .next( constraintsTurnOnStep )
                        .build();
    }

    ...

}

因此,工作由三个步骤组成。第一个和最后一个非常简单。它们只是分别打开和打开目标数据库中的某些约束。中间步骤是核心业务。它读取对源数据库进行去杂化的输入文件,并应用基于表的分区。也就是说,迁移步骤的副本在源数据库的每个表的单独线程上运行:

    @Bean( "partitionerStep" )
    public Step partitionerStep( @Qualifier( "migrationAndAnonymizationStep" ) Step migrationAndAnonymizationStep, TaskExecutor taskExecutor ) {
        return stepBuilderFactory
                    .get( "partitionerStep" )
                    .partitioner( "migrationAndAnonymizationStep", new MultiTablePartitioner( this.model.getEntities() ) )
                    .step( migrationAndAnonymizationStep )
                    .taskExecutor( taskExecutor )
                    .gridSize( this.model.getEntitiesCount() )
                    .build();
    }

最后,迁移步骤配置如下:

    @Bean( "migrationAndAnonymizationStep" )
    public Step migrationAndAnonymizationStep( MigrationAndAnonymizationReader reader, 
                                               MigrationAndAnonymizationProcessor processor, 
                                               MigrationAndAnonymizationWriter writer,  
                                               TaskExecutor taskExecutor ) {

        return stepBuilderFactory
                    .get( "migrationAndAnonymizationStep" )
                    .<Map<String, Object>, Map<String, Object>>chunk( 50 )
                    .reader( reader )
                    .processor( processor )
                    .writer( writer )
                    .taskExecutor( taskExecutor )
                    .throttleLimit( 1 )
                    .build();
    }

MigrationAndAnonymizationReader基本上是JdbcCursorItemReader,由于分区而具有某些配置,而MigrationAndAnonymizationWriter基本上是FlatFileItemWriter的子类,具有一些小的初始化,就像{{1 }}。

作业执行是使用命令行运行的:

MigrationAndAnonymizationReader

我用于检查重新启动功能的测试包括使用与上一个命令类似的命令运行作业,并且在执行过程中(通常需要2分钟来处理我正在使用的测试数据库),我会终止该过程。然后,我执行相同的命令行,我期望作业将重新启动,而不是执行已完成的步骤和未完成的步骤,而是从发生故障之前的位置开始执行。但是,每次尝试此测试时,我看到的是工作从头开始就完全执行了。

那么,我在这里想念什么? Spring Boot / Batch期望我采取什么行动,实现或配置?

1 个答案:

答案 0 :(得分:1)

Spring Batch由于此行而开始新工作:

.incrementer( new RunIdIncrementer() )

这会为每次运行生成唯一的ID。

如果要重新启动作业,则必须确保传递给作业的参数相同。因此,您不能使用RunIdIncrementer