在日常开发过程中,或多或少我们都会涉及到数据报表、统计分析、定时任务之类的应用场景。针对这些场景,我们可以采用Hadoop生态圈中的相关技术。但是Hadoop是一种重量级的实现方案,实际应用过程中存在入门门槛过高、学习周期过长、开发和维护困难等问题,对于某些体量并不是特别大的应用场景而言并建议使用。相反,我们希望找到一种轻量级实现方案来支持日常批处理功能,这就是今天我们要讨论的话题。
那么,如何实现轻量级的批处理呢?让我们先从相关设计理念开始讲起。
轻量级批处理基本架构
在考虑批处理架构之前,我们站在最高的抽象度上,可以把批处理过程看作是一个流程,包括读数据、处理数据和写数据,而这些数据背后是各种数据存储媒介。
批处理架构的抽象过程
和普通应用程序一样,对于如何实现上述流程,我们第一个需要考虑的设计问题是如何确定所需要实现组件之间的职责和功能,这就需要引入分层思想。
分层结构上,批处理架构可以抽象为三个主要层次,基础架构层、核心处理层和应用开发层。基础架构层提供了通用的读、写、处理服务,是对各种数据媒介的操作封装;核心处理层关注于批处理的执行过程,包括对批处理任务和流程的抽象以及如何启动、控制这些任务与流程;应用开发层则包含应用程序需要实现的业务代码。
有了分层架构之后,我们接下来对批处理的处理对象进行建模,从而引出任务(Job)的概念。Job就是批处理的基本对象,每个Job可以包含一个或多个步骤(Step),每个Step负责与具体的外部媒介交互并产生计算结果。
我们知道,对于批处理应用而言,处理的对象并不是一条数据,而是一批数据的集合(Batch)。因此,在读取数据阶段,读取器(Reader)可以单条执行读取操作,并交由数据处理器(Processor)进行转换或过滤处理,但在写数据的过程中,数据写入器(Writer)往往会以一批数据为基本操作单元。
批处理的健壮性
在上面这个时序图中,每一步都可能出现问题。因此,我们需要在出现问题时仍然能够确保批处理流程执行完毕,这就需要引入健壮性(Robustness)的概念。我们可以把批处理的健壮性简单理解为是一种智能化机制,即在长时间不需要开发人员或业务人员干预的情况下仍然可以自动处理各种异常情况。
那么,如何实现健壮性呢?结合批处理的处理特性,我们可以梳理健壮性的不同实现策略,常见的保护三种,即忽略、重试和重启。
忽略的含义在于,对于那些并不影响批处理执行流程的异常情况,我们没有必要停止整个任务,而是可以选择性的忽略这些异常。场景的可以忽略的异常包括数字格式错误等。
有时候,导致任务执行出现异常的原因并不是数据或代码有问题,而是那些瞬态异常,常见的包括网络访问失败或数据库锁等。针对这些瞬态异常,我们可以采取带有重试次数限制的重试策略。
与前面两种情况不同,有时候因为业务处理异常同样会导致批处理执行失败。显然这时候采用忽略或重试策略是解决不了问题的。我们需要暂停任务,然后修复代码问题之后再重新执行任务,这就是重启策略。
在一个成熟的批处理基本架构中,开发人员可以综合使用这三种健壮性处理策略。而这三种策略的采用时机也是可以动态调整的,典型的例子包含:但刚开始出现异常情况时,我们可以采用重试机制,但当重试出现三次之后如果仍然抛出异常,那么我们就需要转为采用重启策略了。
轻量级批处理框架:Spring Batch
介绍完轻量级批处理的基本架构之后,我们来讨论它的实现工具。在Spring家族中,专门针对轻量级批处理技术提供了对应的解决方案,这就是Spring Batch。
Spring Batch基于Spring和Java,实现了批处理的基本架构,并支持批处理健壮性。Spring Batch内置包括文件、数据库、消息中间件、外部服务在内的多种数据读取和写入机制,也对数据处理过程做了转换和过滤抽象。
针对使用场景,Spring Batch也给提供了系统化的支持。使用Spring Batch可以应用于定期提交批处理任务、按顺序处理依赖的任务、部分处理、批处理事务支持以及消息传递等基础设施集成等场景。
Spring Batch的设计理念之一在于以接口形式暴露通用核心的服务并提供了完整的默认实现。Spring Batch的核心接口如下,分别对应批处理的三个主要步骤。可以看到读和处理操作的对象是一个Item,而写操作则使用Item列表。这点与我们前面的分析完全一致。
public interface ItemReader<T> {
T read() throws Exception, UnexpectedInputException, ParseException, NonTransientResourceException;
}
public interface ItemProcessor<I, O> {
O process(I item) throws Exception;
}
public interface ItemWriter<T> {
void write(List<? extends T> items) throws Exception;
}
其中,ItemReader和ItemWriter分别实现数据读取和数据写入,对象可以包括文本文件、XML文件、数据库、服务和JMS等多种形式。
然后,ItemProcessor代表处理器模型,Spring Batch中的数据处理有转换(Transformation)和过滤(Filtering)两种主要的场景。转换的形式有多种,基本的数据状态和数据结构转换比较常见。而过滤的目的是决定是否进行Writer操作。无论是转换还是过滤,Spring Batch都为开发人员提供了扩展接口,我们可以基于业务逻辑实现自定义的复杂机制。
针对批处理的健壮性,Spring Batch也同时支持Skip、Retry和Restart这三种策略。为了实现这三种策略,Spring Batch都Job进行了进一步抽象。对于任何一个Job,运行过程中都存在一种一对多关系,即Job的定义应该只有一份,但可以有多次执行。因此,Spring Batch中针对每个Job会生成一个Job Instance,然后每次Job执行对应一个Job Execution。这样,健壮性策略在过程中会根据Job Instance生成一个新的Job Execution并放在Job Repository中。
上图中的Job Repository保存批处理运行时详细信息,Spring Batch支持In-memory和JDBC两种持久化实现策略。
总结
今天的内容系统分析了轻量级批处理技术的方方面面。我们看到作为一个常见的技术体系,想要实现批处理,开发人员需要考虑的维度也非常多。我们首先站在架构设计的角度,对批处理执行过程进行了抽象,并给出了分层架构。在分层架构的基础上,我们就引出了批处理的健壮性需求,并同样阐述了三种实现方案。基于这些讨论的设计思想,业界也存在一些代表性的轻量级批处理框架。今天的内容我们关注Spring家族的Spring Batch框架,可以看到该框架的实现过程和我们的设计思想是高度一致的。
标签:健壮性,批处理,Spring,Batch,Job,解析,轻量级 From: https://blog.csdn.net/2401_83062316/article/details/139494395