前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Spring Batch 批处理(7) - 异常处理及容错机制

Spring Batch 批处理(7) - 异常处理及容错机制

作者头像
chenchenchen
发布2020-05-26 17:17:38
1.5K0
发布2020-05-26 17:17:38
举报
文章被收录于专栏:chenchenchenchenchenchen

异常处理及重启机制

1.对于chunk类型的Step,spring batch为我们提供了用于管理它的状态

2.状态的管理是通过ItemStream接口来实现的

3.ItemStream接口:

(1)open():每一次step执行会调用

(2)Update():每一个chunk去执行都会调用

(3)Close():所有的chunk执行完毕会调用

![file](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9ncmFwaC5iYWlkdS5jb20vcmVzb3VyY2UvMjIyODc5Y2ZkODQ2YjExZDhlM2VlMDE1ODMyMjgzMzAucG5n?x-oss-process=image/format,png)

构造例子

准备个cvs文件,在第33条数据,添加一条错误名字信息 ;当读取到这条数据时,抛出异常终止程序。

file
file

ItemReader测试代码

代码语言:javascript
复制
@Component("restartDemoReader")
public class RestartDemoReader implements ItemStreamReader<Customer> {
 
    // 记录当前读取的行数
    private Long curLine = 0L;
    // 重启状态初始值
    private boolean restart = false;
 
    private FlatFileItemReader<Customer> reader = new FlatFileItemReader<>();
 
    // 持久化信息到数据库
    private ExecutionContext executionContext;
    RestartDemoReader
    public () {
        
        reader.setResource(new ClassPathResource("restartDemo.csv"));
 
        DelimitedLineTokenizer tokenizer = new DelimitedLineTokenizer();
        tokenizer.setNames(new String[]{"id", "firstName", "lastName", "birthdate"});
 
        DefaultLineMapper<Customer> lineMapper = new DefaultLineMapper<>();
        lineMapper.setLineTokenizer(tokenizer);
        lineMapper.setFieldSetMapper((fieldSet -> {
            return Customer.builder().id(fieldSet.readLong("id"))
                    .firstName(fieldSet.readString("firstName"))
                    .lastName(fieldSet.readString("lastName"))
                    .birthdate(fieldSet.readString("birthdate"))
                    .build();
        }));
        lineMapper.afterPropertiesSet();
 
        reader.setLineMapper(lineMapper);
    }
 
    @Override
    public Customer read() throws Exception, UnexpectedInputException, ParseException,
            NonTransientResourceException {
 
        Customer customer = null;
 
        this.curLine++;
        //如果是重启,则从上一步读取的行数继续往下执行
        if (restart) {
            reader.setLinesToSkip(this.curLine.intValue()-1);
            restart = false;
            System.out.println("Start reading from line: " + this.curLine);
        }
 
        reader.open(this.executionContext);
 
        customer = reader.read();
        //当匹配到wrongName时,显示抛出异常,终止程序
        if (customer != null) {
            if (customer.getFirstName().equals("wrongName"))
                throw new RuntimeException("Something wrong. Customer id: " + customer.getId());
        } else {
            curLine--;
        }
        return customer;
    }
 
    /**
     * 判断是否是重启job
     * @param executionContext
     * @throws ItemStreamException
     */
    @Override
    public void open(ExecutionContext executionContext) throws ItemStreamException {
        this.executionContext = executionContext;
				// 如果是重启job,从数据库读取重启的行数,从重启行数开始重新执行
        if (executionContext.containsKey("curLine")) {
            this.curLine = executionContext.getLong("curLine");
            this.restart = true;
        }
				// 如果不是重启job,初始化行数,从第一行开始执行
				else {
            this.curLine = 0L;
            executionContext.put("curLine", this.curLine.intValue());
        }
 
    }
 
    @Override
    public void update(ExecutionContext executionContext) throws ItemStreamException {
		    // 每执行完一个批次chunk,打印当前行数
        System.out.println("update curLine: " + this.curLine);
        executionContext.put("curLine", this.curLine);
 
    }
 
    @Override
    public void close() throws ItemStreamException {
 
    }
}

Job配置

以10条记录为一个批次,进行读取

代码语言:javascript
复制
@Configuration
public class RestartDemoJobConfiguration {
    @Autowired
    private JobBuilderFactory jobBuilderFactory;
 
    @Autowired
    private StepBuilderFactory stepBuilderFactory;
 
    @Autowired
    @Qualifier("flatFileDemoWriter")
    private ItemWriter<? super Customer> flatFileDemoWriter;
 
    @Autowired
    @Qualifier("restartDemoReader")
    private ItemReader<Customer> restartDemoReader;
 
    @Bean
    public Job restartDemoJob(){
        return jobBuilderFactory.get("restartDemoJob")
                .start(restartDemoStep())
                .build();
 
    }
 
    @Bean
    public Step restartDemoStep() {
        return stepBuilderFactory.get("restartDemoStep")
                .<Customer,Customer>chunk(10)
                .reader(restartDemoReader)
                .writer(flatFileDemoWriter)
                .build();
    }
}

当第一次执行时,程序在33行抛出异常异常,curline值是30;

file
file

这时,可以查询数据库 batch_step_excution表,发现curline值已经以 键值对形式,持久化进数据库(上文以10条数据为一个批次;故33条数据异常时,curline值为30)

file
file

接下来,更新wrongName,再次执行程序;

程序会执行open方法,判断数据库step中map是否存在curline,如果存在,则是重跑,即读取curline,从该批次开始往下继续执行;

file
file
file
file

容错机制

Spring batch的容错机制是一种与事务机制相结合的机制,它主要包括有3种操作:

  • restart
  • restart是针对job来使用,是重启job的一个操作。默认情况下,当任务出现异常时,SpringBatch会结束任务,当使用相同参数重启任务时,SpringBatch会去执行未执行的剩余任务
  • retry
  • retry是对job的某一step而言,处理一条数据item的时候发现有异常,则重试一次该数据item的step的操作。
  • skip
  • skip是对job的某一个step而言,处理一条数据item的时候发现有异常,则跳过该数据item的step的操作。

restart示例代码如下,当第一次执行的时候,上下文中没有该字段,抛出异常,第二次执行,已存在该字段,执行成功

file
file

retry、skip示例如下,更改一下之前step的配置,参考代码如下: ``` @Bean public Step stepForTranscation(StepBuilderFactory stepBuilderFactory, @Qualifier("stepForTranscationReader")ListItemReader reader, @Qualifier("stepForTranscationProcessor")ItemProcessor

}

代码语言:javascript
复制
<br/>


这里设置了允许重试的次数为3次,允许跳过的数据最多为1条,如果job失败了,运行重跑次数最多为3次。

<br/>
在skip后面配置跳过错误的监听器SkipListener

public class MySkipListener implements SkipListener<String, String>{ // 发生读操作跳过错误时,需要执行的监听 public void onSkipInRead(Throwable t){ }

代码语言:javascript
复制
// 发生写操作跳过错误时,需要执行的监听
public void onSkipInWrite(String item, Throwable t){
}

// 处理数据时跳过错误时,需要执行的监听
public void onSkipInProcess (String item, Throwable t){
	System.out.println(item + "occur exception" + t);
}

}

代码语言:javascript
复制
<br/>


重新运行程序,可以得到新的结果:

![file](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9ncmFwaC5iYWlkdS5jb20vcmVzb3VyY2UvMjIyNzJjYzk4ZTk5ZTk4Nzg1NWU3MDE1ODM0MjUzNDUucG5n?x-oss-process=image/format,png)

<br/>

这次可以看到,12条数据中总共有11条数据进入到数据库,而过长的008008008008数据,则因为设置了skip,所以容错机制允许它不进入数据库,这次的Spring batch最终没有因为回滚而中断。

<br/>
查阅一下Spring batch的持久化数据表:

![file](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9ncmFwaC5iYWlkdS5jb20vcmVzb3VyY2UvMjIyNjE5YTg3M2VjNGNkNDE0Y2U1MDE1ODM0MjUzNTUucG5n?x-oss-process=image/format,png)

可以看出,的确是有一条数据被跳过了,但因为是我们允许它跳过的,所以整个job顺利完成,即COMPLETED。



参考:

https://blog.csdn.net/chihe9907/article/details/100601523
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-03-10 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 异常处理及重启机制
  • 容错机制
相关产品与服务
批量计算
批量计算(BatchCompute,Batch)是为有大数据计算业务的企业、科研单位等提供高性价比且易用的计算服务。批量计算 Batch 可以根据用户提供的批处理规模,智能地管理作业和调动其所需的最佳资源。有了 Batch 的帮助,您可以将精力集中在如何分析和处理数据结果上。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档