Estrutura de processamento em lote do Spring Batch

1. Introdução ao SpringBatch

O Spring Batch é uma estrutura leve e abrangente de processamento em lote projetada para oferecer suporte ao desenvolvimento de aplicativos robustos de processamento em lote que são críticos para a operação diária dos sistemas corporativos. O Spring Batch se baseia nos recursos esperados do Spring Framework (produtividade, abordagem de desenvolvimento baseada em POJO e facilidade geral de uso), ao mesmo tempo em que torna mais fácil para os desenvolvedores acessar e usar serviços corporativos de nível superior quando necessário.

O Spring Batch não é uma estrutura de agendamento. Existem muitos planejadores corporativos bons (por exemplo, Quartz, Tivoli, Control-M, etc.) nos mundos comercial e de código aberto. O Spring Batch deve ser usado em conjunto com o agendador, não para substituí-lo.
insira a descrição da imagem aqui

2. Cenários de negócios

Frequentemente encontramos esta situação no desenvolvimento de negócios:
insira a descrição da imagem aqui

O Spring Batch suporta os seguintes cenários de negócios:

  • Envie lotes periodicamente.
  • Processamento em lote simultâneo: Processe trabalhos em paralelo.
  • Processamento acionado por mensagem empresarial em estágios.
  • Processamento em lote massivamente paralelo.
  • Reinicialização manual ou agendada após falha.
  • Processamento sequencial de etapas relacionadas (extensão a lotes orientados a fluxo de trabalho).
  • Processamento parcial: Pular registros (por exemplo, ao reverter).
  • Lote inteiro de transações, aplicável a pequenos lotes ou procedimentos armazenados ou scripts existentes.

3. Conhecimento básico

3.1. Estrutura geral

Documentação oficial: https://docs.spring.io/spring-batch/docs/current/reference/html/index-single.html#domainLanguageOfBatch
insira a descrição da imagem aqui

nome efeito
JobRepository Fornece um mecanismo de persistência para todos os protótipos (Job, JobInstance, Step)
JobLauncher JobLauncher representa uma interface simples para lançar um Job, dado um conjunto de JobParameters
Trabalho Job é uma entidade que encapsula todo o processo em lote
Etapa Step é um objeto de domínio que encapsula um estágio sequencial independente de uma tarefa em lote

3.2. Interface principal

  • ItemReader: é uma abstração que representa a saída de uma etapa,
    um lote ou bloco de itens por vez
  • ItemProcessor: uma abstração que representa o processamento de negócios
    de um item.
  • ItemWriter: é uma abstração que representa a saída de uma etapa,
    um lote ou bloco de itens por vez.
    insira a descrição da imagem aqui
    Geralmente, é entrada → processamento de dados → saída. Uma tarefa define várias etapas e procedimentos de processamento. Uma etapa geralmente abrange ItemReader, ItemProcessor , ItemWriter

4. Prática básica

4.0, apresenta SpringBatch

O arquivo pom introduz springboot

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

O arquivo pom apresenta spring-batch e dependências relacionadas

<dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-batch</artifactId>
    </dependency>

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-validation</artifactId>
    </dependency>

    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
    </dependency>

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>
  </dependencies>


insira a descrição da imagem aqui
Mysql cria o caminho do pacote jar do script sql da tabela de biblioteca dependente: ...\maven\repository\org\springframework\batch\spring-batch-core\4.2.1.RELEASE\spring-batch-core- 4.2.1 . RELEASE.jar !\org\springframework\batch\core\schema-mysql.sql

Iniciar sinalizador de classe @EnableBatchProcessing

@SpringBootApplication
@EnableBatchProcessing
public class SpringBatchStartApplication
{
    
    
    public static void main(String[] args) {
    
    
        SpringApplication.run(SpringBatchStartApplication.class, args);
    }
}

FirstJobDemo

@Component
public class FirstJobDemo {
    
    

    @Autowired
    private JobBuilderFactory jobBuilderFactory;
    @Autowired
    private StepBuilderFactory stepBuilderFactory;

    @Bean
    public Job firstJob() {
    
    
        return jobBuilderFactory.get("firstJob")
                .start(step())
                .build();
    }

    private Step step() {
    
    
        return stepBuilderFactory.get("step")
                .tasklet((contribution, chunkContext) -> {
    
    
                    System.out.println("执行步骤....");
                    return RepeatStatus.FINISHED;
                }).build();
    }
}

4.1. Controle de processo

A. Tarefas de várias etapas

@Bean
public Job multiStepJob() {
    
    
    return jobBuilderFactory.get("multiStepJob2")
            .start(step1())
            .on(ExitStatus.COMPLETED.getExitCode()).to(step2())
            .from(step2())
            .on(ExitStatus.COMPLETED.getExitCode()).to(step3())
            .from(step3()).end()
            .build();
}


private Step step1() {
    
    
    return stepBuilderFactory.get("step1")
            .tasklet((stepContribution, chunkContext) -> {
    
    
                System.out.println("执行步骤一操作。。。");
                return RepeatStatus.FINISHED;
            }).build();
}

private Step step2() {
    
    
    return stepBuilderFactory.get("step2")
            .tasklet((stepContribution, chunkContext) -> {
    
    
                System.out.println("执行步骤二操作。。。");
                return RepeatStatus.FINISHED;
            }).build();
}

private Step step3() {
    
    
    return stepBuilderFactory.get("step3")
            .tasklet((stepContribution, chunkContext) -> {
    
    
                System.out.println("执行步骤三操作。。。");
                return RepeatStatus.FINISHED;
            }).build();
}

B. Execução paralela
Dois fluxos são criados: fluxo1 (incluindo step1 e step2) e flow2 (incluindo step3). Em seguida, por meio do método split de JobBuilderFactory, especifique um executor assíncrono para executar flow1 e flow2 de forma assíncrona (ou seja, em paralelo)

@Component
public class SplitJobDemo {
    
    

    @Autowired
    private JobBuilderFactory jobBuilderFactory;
    @Autowired
    private StepBuilderFactory stepBuilderFactory;

    @Bean
    public Job splitJob() {
    
    
        return jobBuilderFactory.get("splitJob")
                .start(flow1())
                .split(new SimpleAsyncTaskExecutor()).add(flow2())
                .end()
                .build();

    }

    private Step step1() {
    
    
        return stepBuilderFactory.get("step1")
                .tasklet((stepContribution, chunkContext) -> {
    
    
                    System.out.println("执行步骤一操作。。。");
                    return RepeatStatus.FINISHED;
                }).build();
    }

    private Step step2() {
    
    
        return stepBuilderFactory.get("step2")
                .tasklet((stepContribution, chunkContext) -> {
    
    
                    System.out.println("执行步骤二操作。。。");
                    return RepeatStatus.FINISHED;
                }).build();
    }

    private Step step3() {
    
    
        return stepBuilderFactory.get("step3")
                .tasklet((stepContribution, chunkContext) -> {
    
    
                    System.out.println("执行步骤三操作。。。");
                    return RepeatStatus.FINISHED;
                }).build();
    }

    private Flow flow1() {
    
    
        return new FlowBuilder<Flow>("flow1")
                .start(step1())
                .next(step2())
                .build();
    }

    private Flow flow2() {
    
    
        return new FlowBuilder<Flow>("flow2")
                .start(step3())
                .build();
    }
}

C. Decisão da tarefa
O papel do tomador de decisão é especificar o programa para executar diferentes processos de tarefas em diferentes situações. Por exemplo, se hoje for um final de semana, deixe a tarefa executar o passo 1 e o passo 2; se for um dia da semana, execute o passo 1 e passo3.

@Component
public class MyDecider implements JobExecutionDecider {
    
    
    @Override
    public FlowExecutionStatus decide(JobExecution jobExecution, StepExecution stepExecution) {
    
    
        LocalDate now = LocalDate.now();
        DayOfWeek dayOfWeek = now.getDayOfWeek();

        if (dayOfWeek == DayOfWeek.SATURDAY || dayOfWeek == DayOfWeek.SUNDAY) {
    
    
            return new FlowExecutionStatus("weekend");
        } else {
    
    
            return new FlowExecutionStatus("workingDay");
        }
    }
}
@Bean
public Job deciderJob() {
    
    
 return jobBuilderFactory.get("deciderJob")
   .start(step1())
   .next(myDecider)
   .from(myDecider).on("weekend").to(step2())
   .from(myDecider).on("workingDay").to(step3())
   .from(step3()).on("*").to(step4())
   .end()
   .build();
}
private Step step1() {
    
    
 return stepBuilderFactory.get("step1")
   .tasklet((stepContribution, chunkContext) -> {
    
    
    System.out.println("执行步骤一操作。。。");
    return RepeatStatus.FINISHED;
   }).build();
}

private Step step2() {
    
    
 return stepBuilderFactory.get("step2")
   .tasklet((stepContribution, chunkContext) -> {
    
    
    System.out.println("执行步骤二操作。。。");
    return RepeatStatus.FINISHED;
   }).build();
}

private Step step3() {
    
    
 return stepBuilderFactory.get("step3")
   .tasklet((stepContribution, chunkContext) -> {
    
    
    System.out.println("执行步骤三操作。。。");
    return RepeatStatus.FINISHED;
   }).build();
}


private Step step4() {
    
    
 return stepBuilderFactory.get("step4")
   .tasklet((stepContribution, chunkContext) -> {
    
    
    System.out.println("执行步骤四操作。。。");
    return RepeatStatus.FINISHED;
   }).build();
}

D. Aninhamento de Tarefas
Além da tarefa Job poder ser composta por Step ou Flow, também podemos converter múltiplas tarefas Job em uma Step especial, e então atribuí-la a outra tarefa Job, que é o aninhamento de tarefas.

@Component
public class NestedJobDemo {
    
    

    @Autowired
    private JobBuilderFactory jobBuilderFactory;
    @Autowired
    private StepBuilderFactory stepBuilderFactory;
    @Autowired
    private JobLauncher jobLauncher;
    @Autowired
    private JobRepository jobRepository;
    @Autowired
    private PlatformTransactionManager platformTransactionManager;

    // 父任务
    @Bean
    public Job parentJob() {
    
    
        return jobBuilderFactory.get("parentJob")
                .start(childJobOneStep())
                .next(childJobTwoStep())
                .build();
    }


    // 将任务转换为特殊的步骤
    private Step childJobOneStep() {
    
    
        return new JobStepBuilder(new StepBuilder("childJobOneStep"))
                .job(childJobOne())
                .launcher(jobLauncher)
                .repository(jobRepository)
                .transactionManager(platformTransactionManager)
                .build();
    }

    // 将任务转换为特殊的步骤
    private Step childJobTwoStep() {
    
    
        return new JobStepBuilder(new StepBuilder("childJobTwoStep"))
                .job(childJobTwo())
                .launcher(jobLauncher)
                .repository(jobRepository)
                .transactionManager(platformTransactionManager)
                .build();
    }

    // 子任务一
    private Job childJobOne() {
    
    
        return jobBuilderFactory.get("childJobOne")
                .start(
                    stepBuilderFactory.get("childJobOneStep")
                            .tasklet((stepContribution, chunkContext) -> {
    
    
                                System.out.println("子任务一执行步骤。。。");
                                return RepeatStatus.FINISHED;
                            }).build()
                ).build();
    }

    // 子任务二
    private Job childJobTwo() {
    
    
        return jobBuilderFactory.get("childJobTwo")
                .start(
                    stepBuilderFactory.get("childJobTwoStep")
                            .tasklet((stepContribution, chunkContext) -> {
    
    
                                System.out.println("子任务二执行步骤。。。");
                                return RepeatStatus.FINISHED;
                            }).build()
                ).build();
    }
}

4.2, ler dados

Defina Model TestData, o mesmo abaixo

@Data
public class TestData {
    
    
    private int id;
    private String field1;
    private String field2;
    private String field3;
}

Os dados de leitura incluem: leitura de dados de texto, leitura de dados de banco de dados, leitura de dados XML, leitura de dados JSON, etc., verifique você mesmo as informações.

Demonstração de leitura de dados de texto

@Component
public class FileItemReaderDemo {
    
    

    // 任务创建工厂
    @Autowired
    private JobBuilderFactory jobBuilderFactory;
    // 步骤创建工厂
    @Autowired
    private StepBuilderFactory stepBuilderFactory;

    @Bean
    public Job fileItemReaderJob() {
    
    
        return jobBuilderFactory.get("fileItemReaderJob2")
                .start(step())
                .build();
    }

    private Step step() {
    
    
        return stepBuilderFactory.get("step")
                .<TestData, TestData>chunk(2)
                .reader(fileItemReader())
                .writer(list -> list.forEach(System.out::println))
                .build();
    }

    private ItemReader<TestData> fileItemReader() {
    
    
        FlatFileItemReader<TestData> reader = new FlatFileItemReader<>();
        reader.setResource(new ClassPathResource("reader/file")); // 设置文件资源地址
        reader.setLinesToSkip(1); // 忽略第一行

        // AbstractLineTokenizer的三个实现类之一,以固定分隔符处理行数据读取,
        // 使用默认构造器的时候,使用逗号作为分隔符,也可以通过有参构造器来指定分隔符
        DelimitedLineTokenizer tokenizer = new DelimitedLineTokenizer();

        // 设置属性名,类似于表头
        tokenizer.setNames("id", "field1", "field2", "field3");

        // 将每行数据转换为TestData对象
        DefaultLineMapper<TestData> mapper = new DefaultLineMapper<>();
        // 设置LineTokenizer
        mapper.setLineTokenizer(tokenizer);

        // 设置映射方式,即读取到的文本怎么转换为对应的POJO
        mapper.setFieldSetMapper(fieldSet -> {
    
    
            TestData data = new TestData();
            data.setId(fieldSet.readInt("id"));
            data.setField1(fieldSet.readString("field1"));
            data.setField2(fieldSet.readString("field2"));
            data.setField3(fieldSet.readString("field3"));
            return data;
        });
        reader.setLineMapper(mapper);
        return reader;
    }

}

4.3. Dados de saída

Os dados de saída também incluem: leitura de dados de texto, leitura de dados de banco de dados, leitura de dados XML, leitura de dados JSON, etc.

@Component
public class FileItemWriterDemo {
    
    

    @Autowired
    private JobBuilderFactory jobBuilderFactory;
    @Autowired
    private StepBuilderFactory stepBuilderFactory;
    @Resource(name = "writerSimpleReader")
    private ListItemReader<TestData> writerSimpleReader;

    @Bean
    public Job fileItemWriterJob() throws Exception {
    
    
        return jobBuilderFactory.get("fileItemWriterJob")
                .start(step())
                .build();
    }

    private Step step() throws Exception {
    
    
        return stepBuilderFactory.get("step")
                .<TestData, TestData>chunk(2)
                .reader(writerSimpleReader)
                .writer(fileItemWriter())
                .build();
    }

    private FlatFileItemWriter<TestData> fileItemWriter() throws Exception {
    
    
        FlatFileItemWriter<TestData> writer = new FlatFileItemWriter<>();

        FileSystemResource file = new FileSystemResource("D:/code/spring-batch-demo/src/main/resources/writer/writer-file");
        Path path = Paths.get(file.getPath());
        if (!Files.exists(path)) {
    
    
            Files.createFile(path);
        }
        // 设置输出文件路径
        writer.setResource(file);

        // 把读到的每个TestData对象转换为JSON字符串
        LineAggregator<TestData> aggregator = item -> {
    
    
            try {
    
    
                ObjectMapper mapper = new ObjectMapper();
                return mapper.writeValueAsString(item);
            } catch (JsonProcessingException e) {
    
    
                e.printStackTrace();
            }
            return "";
        };

        writer.setLineAggregator(aggregator);
        writer.afterPropertiesSet();
        return writer;
    }

}

4.5. Dados de processamento

@Component
public class ValidatingItemProcessorDemo {
    
    

    @Autowired
    private JobBuilderFactory jobBuilderFactory;
    @Autowired
    private StepBuilderFactory stepBuilderFactory;
    @Resource(name = "processorSimpleReader")
    private ListItemReader<TestData> processorSimpleReader;

    @Bean
    public Job validatingItemProcessorJob() throws Exception {
    
    
        return jobBuilderFactory.get("validatingItemProcessorJob3")
                .start(step())
                .build();
    }

    private Step step() throws Exception {
    
    
        return stepBuilderFactory.get("step")
                .<TestData, TestData>chunk(2)
                .reader(processorSimpleReader)
                .processor(beanValidatingItemProcessor())
                .writer(list -> list.forEach(System.out::println))
                .build();
    }

//    private ValidatingItemProcessor<TestData> validatingItemProcessor() {
    
    
//        ValidatingItemProcessor<TestData> processor = new ValidatingItemProcessor<>();
//        processor.setValidator(value -> {
    
    
//            // 对每一条数据进行校验
//            if ("".equals(value.getField3())) {
    
    
//                // 如果field3的值为空串,则抛异常
//                throw new ValidationException("field3的值不合法");
//            }
//        });
//        return processor;
//    }

    private BeanValidatingItemProcessor<TestData> beanValidatingItemProcessor() throws Exception {
    
    
        BeanValidatingItemProcessor<TestData> beanValidatingItemProcessor = new BeanValidatingItemProcessor<>();
        // 开启过滤,不符合规则的数据被过滤掉;
//        beanValidatingItemProcessor.setFilter(true);
        beanValidatingItemProcessor.afterPropertiesSet();
        return beanValidatingItemProcessor;
    }

}

4.6. Agendamento de tarefas

Ele pode cooperar com quartzo ou xxljob para realizar a execução da tarefa de temporização

@RestController
@RequestMapping("job")
public class JobController {
    
    

    @Autowired
    private Job job;
    @Autowired
    private JobLauncher jobLauncher;

    @GetMapping("launcher/{message}")
    public String launcher(@PathVariable String message) throws Exception {
    
    
        JobParameters parameters = new JobParametersBuilder()
                .addString("message", message)
                .toJobParameters();
        // 将参数传递给任务
        jobLauncher.run(job, parameters);
        return "success";
    }
}

Acho que você gosta

Origin blog.csdn.net/weixin_43114209/article/details/131527661
Recomendado
Clasificación