此版本仍在开发中,尚不被认为是稳定的。对于最新的稳定版本,请使用 Spring Batch 文档 5.2.2! |
Batch 的领域语言
对于任何有经验的批处理架构师来说,批处理的总体概念用于
Spring Batch 应该熟悉且舒适。有“作业”和“步骤”,以及
开发人员提供的处理单元称为ItemReader
和ItemWriter
.然而
由于 Spring 模式、作、模板、回调和习语,有
以下机会:
-
在遵守明确关注点分离方面有了显着改善。
-
清晰划分的架构层和作为接口提供的服务。
-
简单和默认的实现,允许快速采用和易于使用 开箱即用。
-
显著增强的可扩展性。
下图是批处理参考体系结构的简化版本,其中 已经使用了几十年。它概述了构成 批处理的领域语言。这个架构框架是一个蓝图,它具有 通过过去几代的数十年实施得到证明 平台(大型机上的 COBOL、Unix 上的 C,以及现在的 Java 任何地方)。JCL 和 COBOL 开发人员 可能与 C、C# 和 Java 开发人员一样熟悉这些概念。Spring Batch 提供层、组件和技术的物理实现 服务常见于健壮、可维护的系统中,用于解决 使用基础设施和扩展创建从简单到复杂的批处理应用程序 以满足非常复杂的加工需求。

上图突出显示了构成领域语言的关键概念
春季批次。一个Job
有一个或多个步骤,每个步骤只有一个ItemReader
,
可选的ItemProcessor
,以及一个ItemWriter
.作业已作(已启动、已停止等)
使用JobOperator
,有关当前正在运行的进程的元数据存储在 和
从JobRepository
.
工作
本节介绍与批处理作业概念相关的构造型。一个Job
是一个
封装整个批处理的实体。与其他Spring一样
项目,一个Job
与 XML 配置文件或基于 Java 的
配置。此配置可称为“作业配置”。然而Job
只是整个层次结构的顶部,如下图所示:

在 Spring Batch 中,一个Job
只是一个容器Step
实例。它结合了多个
在逻辑上属于流中的步骤,并允许配置属性
全局到所有步骤,例如可重启性。作业配置包含:
-
作业的名称。
-
定义和排序
Step
实例。 -
作业是否可重新启动。
-
Java
-
XML
对于那些使用 Java 配置的用户,Spring Batch 提供了默认的实现
这Job
界面,以SimpleJob
类,它创建了一些标准
功能之上Job
.使用基于 Java 的配置时,集合
builders 可用于实例化Job
,如下所示
示例显示:
@Bean
public Job footballJob(JobRepository jobRepository) {
return new JobBuilder("footballJob", jobRepository)
.start(playerLoad())
.next(gameLoad())
.next(playerSummarization())
.build();
}
对于那些使用 XML 配置的用户,Spring Batch 提供了Job
界面,以SimpleJob
类,它创建了一些标准
功能之上Job
.但是,批处理命名空间抽象化了
直接实例化它。相反,您可以使用<job>
元素,作为
以下示例显示:
<job id="footballJob">
<step id="playerload" next="gameLoad"/>
<step id="gameLoad" next="playerSummarization"/>
<step id="playerSummarization"/>
</job>
作业实例
一个JobInstance
指逻辑作业运行的概念。考虑一个批处理作业
应该在一天结束时运行一次,例如EndOfDay
Job
从前面
图。有一个EndOfDay
作业,但每次单独运行Job
必须是
单独跟踪。就这份工作而言,有一个合乎逻辑的JobInstance
每天。
例如,有 1 月 1 日运行、1 月 2 日运行,依此类推。如果 1 月 1 日
run 第一次失败,第二天再次运行,它仍然是 1 月 1 日的运行。
(通常,这也与它正在处理的数据相对应,即 1 月
第一次运行处理 1 月 1 日的数据)。因此,每个JobInstance
可以有多个
执行 (JobExecution
在本章后面会更详细地讨论),并且仅
一JobInstance
(对应于特定的Job
并识别JobParameters
)可以
在给定时间运行。
定义JobInstance
对要加载的数据完全没有影响。
这完全取决于ItemReader
实现以确定如何加载数据。为
示例,在EndOfDay
场景,数据上可能有一个列指示effective date
或schedule date
数据所属的。所以,1 月 1 日的运行
将仅加载来自第 1 次的数据,而 1 月 2 日的运行将仅使用来自
2. 因为这个决定很可能是一个商业决策,所以它留给ItemReader
来决定。但是,使用相同的JobInstance
确定是否
“状态”(即ExecutionContext
,本章稍后将讨论)
使用以前的执行。使用新的JobInstance
意思是“从
开始“,使用现有实例通常意味着”从离开的地方开始”
关闭“。
作业参数
讨论过JobInstance
以及它与Job
,自然而然地要问的问题
是:“一个怎么JobInstance
与另一个区别?答案是:JobParameters
.一个JobParameters
对象包含一组用于启动批处理的参数
工作。它们可用于识别,甚至可以在运行过程中用作参考数据,因为
下图显示:

在前面的示例中,有两个实例,一个用于 1 月 1 日,另一个用于 1 月 1 日
对于1月2日,真的只有一个Job
,但它有两个JobParameter
对象:
一个以 01-01-2017 的作业参数启动,另一个以
参数为 01-02-2017。因此,合约可以定义为:JobInstance
= Job
+ 识别JobParameters
.这允许开发人员有效地控制JobInstance
定义,因为它们控制传入的参数。
并非所有作业参数都有助于识别JobInstance .默认情况下,它们会这样做。但是,该框架也允许提交
的Job 使用不有助于JobInstance . |
作业执行
一个JobExecution
指的是单次尝试运行作业的技术概念。一
执行可能以失败或成功告终,但JobInstance
对应于给定的
除非执行成功完成,否则执行不被视为完成。
使用EndOfDay
Job
前面描述的示例,请考虑JobInstance
为
01-01-2017 第一次运行时失败了。如果再次运行相同的
将作业参数标识为第一次运行(01-01-2017),一个新的JobExecution
是
创建。然而,仍然只有一个JobInstance
.
一个Job
定义作业是什么以及如何执行它,以及JobInstance
是一个纯粹的组织对象,将执行分组在一起,主要是为了启用正确的restart 语义。 一个JobExecution
但是,是在运行期间实际发生的,并且包含更多必须控制的属性并持久化,如下表所示:
属性 |
定义 |
|
一个 |
|
一个 |
|
一个 |
|
这 |
|
一个 |
|
一个 |
|
包含需要在 执行。 |
|
执行期间遇到的异常列表 |
这些属性很重要,因为它们是持久化的,并且可以完全用于
确定执行的状态。例如,如果EndOfDay
01-01 的作业是
在晚上 9:00 执行并在 9:30 失败时,批处理中将进行以下条目
元数据表:
JOB_INST_ID |
JOB_NAME |
1 |
EndOfDayJob |
JOB_EXECUTION_ID |
TYPE_CD |
KEY_NAME |
DATE_VAL |
识别 |
1 |
日期 |
附表。日期 |
2017-01-01 |
true |
JOB_EXEC_ID |
JOB_INST_ID |
START_TIME |
END_TIME |
地位 |
1 |
1 |
2017-01-01 21:00 |
2017-01-01 21:30 |
失败 |
为了清楚起见,列名可能已被缩写或删除,并且 格式。 |
现在作业失败了,假设花了整晚的时间才出现问题
确定,因此“批处理窗口”现在已关闭。进一步假设窗口
从晚上 9:00 开始,作业将在 01-01 再次启动,从中断的地方开始,并且
9:30 圆满完成。因为现在是第二天,01-02 的工作肯定是
也运行,紧接着在 9:31 开始并在正常状态下完成
小时时间为 10:30。没有要求JobInstance
在之后被踢开
另一个,除非两个作业有可能尝试访问相同的数据,
导致数据库级别锁定问题。这完全取决于调度程序
确定Job
应该运行。由于它们是分开的JobInstances
Spring
Batch 不会尝试阻止它们并发运行。(尝试运行
相同JobInstance
而另一个已经在运行,则会导致JobExecutionAlreadyRunningException
被扔)。现在应该有一个额外的条目
在JobInstance
和JobParameters
表格和两个额外的条目JobExecution
表,如下表所示:
JOB_INST_ID |
JOB_NAME |
1 |
EndOfDayJob |
2 |
EndOfDayJob |
JOB_EXECUTION_ID |
TYPE_CD |
KEY_NAME |
DATE_VAL |
识别 |
1 |
日期 |
附表。日期 |
2017-01-01 00:00:00 |
true |
2 |
日期 |
附表。日期 |
2017-01-01 00:00:00 |
true |
3 |
日期 |
附表。日期 |
2017-01-02 00:00:00 |
true |
JOB_EXEC_ID |
JOB_INST_ID |
START_TIME |
END_TIME |
地位 |
1 |
1 |
2017-01-01 21:00 |
2017-01-01 21:30 |
失败 |
2 |
1 |
2017-01-02 21:00 |
2017-01-02 21:30 |
完成 |
3 |
2 |
2017-01-02 21:31 |
2017-01-02 22:29 |
完成 |
为了清楚起见,列名可能已被缩写或删除,并且 格式。 |
步
一个Step
是一个域对象,它封装了批处理的独立顺序阶段
工作。因此,每个Job
完全由一个或多个步骤组成。一个Step
包含
定义和控制实际批处理所需的所有信息。这
必然是一个模糊的描述,因为任何给定的内容Step
位于
开发人员的自由裁量权编写Job
.一个Step
可以像
开发商的愿望。一个简单的Step
可能会将数据从文件加载到数据库中,
需要很少或不需要代码(取决于所使用的实现)。更复杂的Step
可能具有作为处理的一部分应用的复杂业务规则。如
使用Job
一个Step
有一个个体StepExecution
与独特的JobExecution
,如下图所示:

步骤执行
一个StepExecution
表示执行Step
.一个新的StepExecution
每次创建Step
运行,类似于JobExecution
.但是,如果某个步骤失败
执行,因为它之前的步骤失败,因此不会为它保留任何执行。一个StepExecution
仅当其Step
实际上已经开始了。
Step
执行由StepExecution
类。每次执行
包含对其相应步骤的引用,并且JobExecution
和交易相关
数据,例如提交和回滚计数以及开始和结束时间。此外,每个步骤
执行包含一个ExecutionContext
,其中包含开发人员需要的任何数据
在批处理运行中持久存在,例如所需的统计信息或状态信息
重新启动。下表列出了StepExecution
:
属性 |
定义 |
|
一个 |
|
一个 |
|
一个 |
|
这 |
|
包含需要在 执行。 |
|
已成功读取的项目数。 |
|
已成功写入的项数。 |
|
已为此执行提交的事务数。 |
|
业务事务由 |
|
次数 |
|
次数 |
|
已通过 |
|
次数 |
执行上下文
一ExecutionContext
表示持久化的键/值对的集合,并且
由框架控制,为开发人员提供一个存储持久性
作用域限定为StepExecution
object 或JobExecution
对象。(对于那些
熟悉 Quartz,它非常类似于JobDataMap
.)最好的用法示例是
促进重启。以平面文件输入为例,在处理单个
行,框架会定期保留ExecutionContext
在提交点。行为
所以让ItemReader
存储其状态,以防在运行过程中发生致命错误
或者即使停电。所需要做的就是输入当前的行数
读取上下文,如以下示例所示,框架会执行
休息:
executionContext.putLong(getKey(LINES_READ_COUNT), reader.getPosition());
使用EndOfDay
示例来自Job
stereotypes 部分作为示例,假设那里有
是一步,loadData
,将文件加载到数据库中。在第一次失败运行后,
元数据表将如以下示例所示:
JOB_INST_ID |
JOB_NAME |
1 |
EndOfDayJob |
JOB_INST_ID |
TYPE_CD |
KEY_NAME |
DATE_VAL |
1 |
日期 |
附表。日期 |
2017-01-01 |
JOB_EXEC_ID |
JOB_INST_ID |
START_TIME |
END_TIME |
地位 |
1 |
1 |
2017-01-01 21:00 |
2017-01-01 21:30 |
失败 |
STEP_EXEC_ID |
JOB_EXEC_ID |
STEP_NAME |
START_TIME |
END_TIME |
地位 |
1 |
1 |
loadData |
2017-01-01 21:00 |
2017-01-01 21:30 |
失败 |
STEP_EXEC_ID |
SHORT_CONTEXT |
1 |
{件数=40321} |
在前面的案例中,Step
运行30分钟,处理了40321个“块”,其中
将表示在此方案中文件中的行。此值在每个
commit,并且可以包含与ExecutionContext
.在提交之前收到通知需要各种StepListener
实现(或ItemStream
),将对此进行更详细的讨论
本指南后面。与前面的示例一样,假设Job
是
第二天重新启动。重新启动时,来自ExecutionContext
之
最后一次运行是从数据库中重构的。当ItemReader
打开后,它可以
检查它在上下文中是否有任何存储状态,并从那里初始化自身,
如以下示例所示:
if (executionContext.containsKey(getKey(LINES_READ_COUNT))) {
log.debug("Initializing for restart. Restart data is: " + executionContext);
long lineCount = executionContext.getLong(getKey(LINES_READ_COUNT));
LineReader reader = getReader();
Object record = "";
while (reader.getPosition() < lineCount && record != null) {
record = readLine();
}
}
在这种情况下,前面的代码运行后,当前行为 40,322,让Step
从上次中断的地方重新开始。您还可以使用ExecutionContext
为
需要保留的有关运行本身的统计信息。例如,如果平面文件
包含跨多行存在的处理订单,则可能需要
存储已处理的订单数量(与
行读取),以便可以在Step
与总数
在正文中处理的订单。框架为开发人员处理存储此内容,
以正确限定其对个人的范围JobInstance
.这可能非常困难
知道现有的ExecutionContext
应该使用与否。例如,使用EndOfDay
上面的例子,当 01-01 运行第二次再次开始时,
框架认识到它是相同的JobInstance
和个人Step
基础
将ExecutionContext
,并将其交给数据库(作为StepExecution
) 设置为Step
本身。相反,对于 01-02 运行,框架
认识到它是一个不同的实例,因此必须将空上下文交给Step
.该框架为以下项目做出了许多此类决定
developer,以确保在正确的时间向他们提供状态。这也很重要
请注意,恰好一个ExecutionContext
存在StepExecution
在任何给定时间。
的客户ExecutionContext
应该小心,因为这会创建一个共享的
keyspace。因此,在输入值时应小心,以确保没有数据
覆盖。但是,Step
在上下文中绝对不存储任何数据,因此没有
对框架产生不利影响的方式。
请注意,至少有一个ExecutionContext
每JobExecution
每个人一个StepExecution
.例如,考虑以下内容
代码片段:
ExecutionContext ecStep = stepExecution.getExecutionContext();
ExecutionContext ecJob = jobExecution.getExecutionContext();
//ecStep does not equal ecJob
正如评论中所指出的,ecStep
不等于ecJob
.他们是两个不同的ExecutionContexts
.作用域限定为Step
保存在Step
,而作用域为 Job 的 Job 保存在每个Step
执行。
在ExecutionContext ,则所有非瞬态条目必须是Serializable .
执行上下文的正确序列化支持步骤和作业的重启功能。
如果您使用本机不可序列化的键或值,则需要
采用量身定制的序列化方法。无法序列化执行上下文
可能会危及状态持久化过程,使失败的作业无法正确恢复。 |
作业存储库
JobRepository
是前面提到的所有刻板印象的持久性机制。
它为JobLauncher
,Job
和Step
实现。当Job
首次启动时,一个JobExecution
是从存储库中获取的。此外,在
执行过程,StepExecution
和JobExecution
实现被持久化
通过将它们传递到存储库。
-
Java
-
XML
使用 Java 配置时,@EnableBatchProcessing
注释提供了一个JobRepository
作为自动配置的组件之一。
Spring Batch XML 命名空间支持配置JobRepository
实例
使用<job-repository>
标记,如以下示例所示:
<job-repository id="jobRepository"/>
作业运算符
JobOperator
表示用于启动、停止和重新启动等作的简单接口
jobs,如以下示例所示:
public interface JobOperator {
JobExecution start(Job job, JobParameters jobParameters) throws Exception;
JobExecution startNextInstance(Job job) throws Exception;
boolean stop(JobExecution jobExecution) throws Exception;
JobExecution restart(JobExecution jobExecution) throws Exception;
JobExecution abandon(JobExecution jobExecution) throws Exception;
}
一个Job
以一组给定的JobParameters
.预计实现将获得
一个有效的JobExecution
从JobRepository
并执行Job
.
项目阅读器
ItemReader
是一个抽象,表示对输入的检索Step
一
一次项目。当ItemReader
已经用尽了它能提供的物品,它
通过返回null
.您可以找到有关ItemReader
接口及其
Readers And Writers 中的各种实现。
项目编写器
ItemWriter
是一个抽象,表示Step
、一个批次或块
一次的项目。通常,一个ItemWriter
不知道它应该输入的
receive next,并且仅知道在其当前调用中传递的项。您可以找到更多
关于ItemWriter
接口及其在读写器中的各种实现。
项目处理器
ItemProcessor
是表示项目的业务处理的抽象。
虽然ItemReader
读取一项,并且ItemWriter
写入一个项目,则ItemProcessor
提供一个接入点来转换或应用其他业务处理。
如果在处理项目时,确定该项目无效,则返回null
表示不应写出该项目。您可以找到有关ItemProcessor
Readers And Writers 中的界面。
批处理命名空间
前面列出的许多领域概念需要在 Spring 中配置ApplicationContext
.虽然上面有接口的实现,但你可以
在标准 bean 定义中使用时,已提供命名空间以方便
配置,如以下示例所示:
<beans:beans xmlns="http://www.springframework.org/schema/batch"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/batch
https://www.springframework.org/schema/batch/spring-batch.xsd">
<job id="ioSampleJob">
<step id="step1">
<tasklet>
<chunk reader="itemReader" writer="itemWriter" commit-interval="2"/>
</tasklet>
</step>
</job>
</beans:beans>
只要声明了批处理命名空间,就可以使用其任何元素。您可以找到更多
有关配置作业的信息,请参阅配置和运行作业。您可以找到有关配置Step
在配置步骤中。