Spring Batch

1. SpringBatch 介绍

Spring Batch是spring的一个子项目,基于spring框架开发。提供了大量的可重用组件,包括日志、追踪、事务、任务 作业统计、重启、跳过等。能够处理简单的、复杂的和大数据量的批处理作业。Spring Batch只关注处理任务相关的问题 ,如事务,并发,监控等,需要调度框架来协作构建完成批处理任务(如 利用Quartz定时调度框架进行任务调度)

Spring Batch 框架组成

1)、jobLauncher:任务启动器(该接口只有一个run方法)、通过它来启动任务、可以看做是程序的入口.

2)、job:代表着一个具体的任务

3)、step:代表着一个具体的步骤,一个job可以包含多个step,每个step都有一个ItemReader(读取数据),一个ItemProcessor(处理数据),和一个ItemWriter(写入数据)。在实际场景中,一个任务可能会很复杂,这个时候可以将任务拆分为多个Step,分别对这些step进行管理(复杂任务简单化),step默认是串行执行,也可以更改为并行执行。

4)、jobRepository:存储数据的地方,可以看做是一个数据库的接口,在任务执行的时候需要通过他来记录任务的状态等信息。

关键词描述: 

Spring Batch 流程介绍

每个batch中都会包含 一个Job,Job就像一个容器,这个容器里装了若干step,Batch中实际工作的就是这些Step,(读取数据-->处理数据-->写入数据)。外部控制器(定时器控制)调用JobLauncher启动一个Job,Job调用自己的Step去实现对数据的操作,Step树立完成后再将处理结果一步步返回给上一层,这就是Batch处理实现的一个简单流程

 实际应用中数据来源不同,实现接口不同,但读写过程操作近似一致。选择哪个实现接口,根据需求来确定。

此处指列举几个常用的。

  • JdbcPagingItemReader :从数据库中读取数据
  • FlatFileItemReader:从CVS文件中读取数据
  • StaxEventItemReader:从XML文件中读取数据
  • MultiResourceItemReader:从多个文件读取数据

2. Spring Batch 异常处理

1). skip 跳过

在一个step中,不管reader还是process,还是write,出现了指定的错误都可以跳过,继续执行后面的数据。

public Step barrageTextOpinionArchiveSentimentStep() throws InstantiationException, IllegalAccessException {
        return stepBuilderFactory.get("barrageTextOpinionArchiveSentimentStep")
                .<BarrageTextOpinion, BarrageTextOpinion> chunk(SENTIMENT_CHUNK_SIZE)
                .reader(barrageTextOpinionArchiveSentimentFileReader())
                .writer(barrageTextOpinionArchiveSentimentChainWriter())
                .faultTolerant()
                .processorNonTransactional()
                .skip(Exception.class)   //跳过IOException类型的异常
                .skipLimit(100)   //指定做多跳过5次
                .listener(stepStatusListener)
                .listener(exceptionLoggingSkipListener)
                .allowStartIfComplete(true)
                .build();
    }

2). retry 重试

当Spring Batch在处理批量时,有时因为某些原因,使批量在第一次执行时出错,比如锁表之类的,出错之后,可以进行(重试)多次执行出错的地方,如果重试每次都失败,批量才失败。

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

    @Bean
    public Step chunkStep(){
        return stepBuilderFactory.get("chunkStep1")
                .chunk(3) // 块大小                                          
                .reader(fileItemReader())                           
                .writer(fileItemWriter())
                .faultTolerant()
                /**.retry(LockObtainFailedException.class)
                .retryLimit(1)*/
                .retry(FlatFileParseException.class) 
//遇到FlatFileParseException类型的异常时,运行重复执行3次,如果3次都失败,批量才失败
                .retryLimit(3)
                .noRetry(NullPointerException.class) 
//遇到NullPointerException类型的异常时,不重复执行
                .allowStartIfComplete(true)  
                .build();
    }

skip与retry的区别:retry不能对reader抛出的异常进行retry,只能对process或者write抛出的异常进行retry;skip可以对reader或者process或者write抛出的异常进行skip。

3). restart 重启

1、正常情况下,重启相同参数的job,会报错,提示该job已存在。一般设置job id自增或在job参数中加上系统时间进行区分,完成重启。
2、在step中阻止重启

如果Job允许重启,但step不允许重启,当重启job后,会跳过不允许重启的step,不会报错

@Bean
    public Job demoTestJob(){
        return jobBuilderFactory.get("demoTestJob")
                .listener(jobStatusListener)
                .start(demoTestStep())
                .build();

    }
    @Bean
    public Step demoTestStep(){
        return stepBuilderFactory.get("demoTestStep")
                .listener(stepStatusListener)
                .<User,User>chunk(1)
                .reader(demoReader())
                .writer(demoWriter())
//              .allowStartIfComplete(true) 
                .allowStartIfComplete(false) 
                .build();
    }

当重启Job后,在日志表中可以看到job的status是COMPLETED,exit_code是NOOP,exit_message是 All steps already completed or no steps configured for this job.

3、重启次数

    @Bean
    public Job demoTestJob(){
        return jobBuilderFactory.get("demoTestJob")
                .listener(jobStatusListener)
                .start(demoTestStep())
                .build();

    }
    @Bean
    public Step demoTestStep(){
        return stepBuilderFactory.get("demoTestStep")
                .listener(stepStatusListener)
                .<User,User>chunk(1)
                .reader(demoReader())
                .writer(demoWriter())
                .startLimit(10) //设置有异常时,重启次数是3次,如果没异常,不重启
                .build();
    }

当该job作业没有异常时,job不管运行多少次,step都只在第一次运行;如果该job作业有异常,当job执行第一次在step中抛出异常后,当后面重启job后,step从上次异常中断的地方重启,如果还是异常,一直到3次后,就不再重启,提示已经超过最大执行次数:Maximum start limit exceeded for step: chunkStep1StartMax: 10 。

4、job成功后,也允许重启
前三种都是当job出现异常,重启后,从抛出异常的地方继续执行。而下面情况,每次重启job后,都是从开始的地方执行。

    @Bean
    public Job demoTestJob(){
        return jobBuilderFactory.get("demoTestJob")
                .listener(jobStatusListener)
                .start(demoTestStep())
                .build();

    }
    @Bean
    public Step demoTestStep(){
        return stepBuilderFactory.get("demoTestStep")
                .listener(stepStatusListener)
                .<User,User>chunk(1)
                .reader(demoReader())
                .writer(demoWriter())
                .allowStartIfComplete(true) // job任务执行完成后,重启再次执行该任务
                .build();
    }

3.自定义监听器 

jobStatusListener

package com.lxj.config;

import lombok.extern.apachecommons.CommonsLog;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobExecutionListener;
import org.springframework.batch.core.JobParameter;
import org.springframework.batch.core.JobParameters;
import org.springframework.stereotype.Component;

import java.util.Iterator;
import java.util.Map;

@CommonsLog
@Component
public class JobStatusListener implements JobExecutionListener {
    @Override
    public void beforeJob(JobExecution jobExecution) {

    }

    @Override
    public void afterJob(JobExecution jobExecution) {
        StringBuilder buf = new StringBuilder();
        buf.append("\nJob " + jobExecution.getJobInstance().getJobName() + " \n");
        buf.append("  Started     : " + jobExecution.getStartTime() + "\n");
        buf.append("  Finished    : " + jobExecution.getEndTime() + "\n");
        buf.append("  Exit-Code   : " + jobExecution.getExitStatus().getExitCode() + "\n");
        buf.append("  Exit-Descr. : " + jobExecution.getExitStatus().getExitDescription() + "\n");

        buf.append("Job-Parameter : \n");
        JobParameters jp = jobExecution.getJobParameters();
        for (Iterator<Map.Entry<String, JobParameter>> iter = jp.getParameters().entrySet().iterator(); iter.hasNext(); ) {
            Map.Entry<String, JobParameter> entry = iter.next();
            buf.append("  " + entry.getKey() + "=" + entry.getValue() + "\n");
        }
        log.info(buf.toString());
    }
}

StepStatusListener 

package com.lxj.config;

import lombok.extern.apachecommons.CommonsLog;
import org.springframework.batch.core.ExitStatus;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.StepExecutionListener;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

@CommonsLog
@Component
public class StepStatusListener implements StepExecutionListener {
    @Override
    public void beforeStep(StepExecution stepExecution) {

    }

    @Override
    public ExitStatus afterStep(StepExecution stepExecution) {
        StringBuilder buf = new StringBuilder();

        long duration = System.currentTimeMillis() - stepExecution.getStartTime().getTime();

        buf.append("Step " + stepExecution.getStepName() + " \n");
        buf.append("  Spent (m) : " + TimeUnit.MILLISECONDS.toMinutes(duration) + "\n");
        buf.append("  WriteCount: " + stepExecution.getWriteCount() + "\n");
        buf.append("  Commits   : " + stepExecution.getCommitCount() + "\n");
        buf.append("  SkipCount : " + stepExecution.getSkipCount() + "\n");
        buf.append("  Rollbacks : " + stepExecution.getRollbackCount() + "\n");
        buf.append("  Filter    : " + stepExecution.getFilterCount() + "\n");
        log.info(buf.toString());

        return stepExecution.getExitStatus();
    }
}

Others' knowledge of springbatch

https://blog.csdn.net/guo_xl/article/details/83444983

https://blog.csdn.net/cxy_chh/article/details/92062259?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task

https://www.jdon.com/springboot/spring-batch-partition.html

https://blog.csdn.net/neweastsun/article/details/88866837

https://blog.51cto.com/13501268/2177746

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值