Spring Batch 2.0新功能概述

工程 | Dave Syer | 2008年10月21日 | ...

在本文中,我们概述了Spring Batch 2.0的主要主题,并重点介绍了与1.x版本的变化。新版本的开发工作正在顺利进行,上周发布了M2版本,并且我们获得了大量关注,因此现在似乎是提供一些指针的好时机。

Spring Batch 2.0 主题

新版本的四个主要主题是

  • Java 5和Spring 3.0
  • 非顺序执行
  • 可伸缩性
  • 配置:注解和XML命名空间
因此,我们将分别介绍每个领域,描述它们意味着什么以及这些变化对Spring Batch现有用户的影响。下面有更多关于已实现功能的详细信息,这些功能主要集中在第一类,并在其他领域提供了一些支持性功能。

Spring Batch 2.0.0.M2 在项目物理布局方面没有变化(下载地址不变,Java 包的基本布局也未变)。我们没有移除任何功能,但我们借此机会重新设计了一些 API,并且对于从 1.x 更新项目的用户来说,有一些小的改动。Spring Batch 尚不成熟,并且我们正在添加一些相当大的功能,因此我们决定进行主版本升级是一个进行一些清理的好机会。我们预计升级不会有任何困难,如果您是现有用户,本文将帮助您了解这些变化。

Java 5和Spring 3.0

正如您可能知道的,Spring 3.0 将是 Spring 的第一个主要版本,它将完全支持 Java 5(我将留给 Juergen 和 Arjen 详细阐述)。现在 Sun 已经给 JDK 1.4 打上了“服务生命终结”的标签,这似乎是恰当的,而且 Spring 3.0 中有一些很棒的新功能是我们想要利用的。

类型安全

Spring Batch 2.0.0.M1 版本的大部分工作都集中在将现有代码转换为 Java 5,尽可能地利用泛型和参数化类型。这为框架用户提供了更好的编程体验,允许在编译时进行类型安全检查,并最终降低了使用 Spring Batch 的项目的维护成本。例如,在ItemReader(Spring Batch 中的一个核心接口和用户扩展点)中,我们现在有了一个类型安全的read方法

public interface ItemReader<S> {
    S read();
}

方法。需要注意的另一点是,旧的(1.x)框架回调mark()还是reset()Step实现内部处理。

面向块的处理

类似的改动,以及一个稍显激进的改动,也出现在配套的ItemWriter接口中,该接口用于框架写入数据。


public interface ItemWriter<T> {
    void write(List<? extends T> items);
}

旧的框架回调,如flush()还是clear()write()方法有了新的签名。这里的重点是,我们已经在框架内部转向了面向块的处理范例。对于批处理框架来说,这实际上比旧的项目导向方法更自然,因为出于性能原因,我们经常需要缓冲和刷新,而旧的接口使得用户在这方面感到不便。现在您可以在write()方法实现。

Step Factory Bean 更改

面向块的处理方法的一个副作用是步骤工厂 Bean 实现的更改。旧的SkipLimitStepFactoryBean已被重命名为FaultTolerantStepFactoryBean,并且在大多数常见用例中,它仍然可以作为旧工厂 Bean 的替代品。默认情况下,它创建的步骤实现会在回滚时缓冲输入项,以便新的ItemReader还是ItemWriter接口能够与非事务性输入源(如文件)正确配合使用。对于回滚后会重新呈现项的输入源,客户端必须在工厂 Bean 中设置一个标志(在 M2 版本中,该标志名为isReaderTransactional)——JmsItemReader是框架中唯一相关的读取器,并且很少有项目使用它,所以这不是一个大的改动。

业务处理

一个相关的で新功能是ItemProcessor:

public interface ItemProcessor<S,T> {
    T process(S item);
}

的出现。在 1.x 中,类型为S的输入项和类型为T的输出项之间的转换必须隐藏在其他参与者(通常是ItemWriter相同的重要性级别。1.x 的用户可能会在这里识别出旧的ItemReader还是ItemWriter)中。现在我们已经将这个关注点泛化,并将其置于与它的同级接口ItemTransformer接口的痕迹,该接口现在已被移除。

更实用的 Tasklet 接口

很多人在查看 Spring Batch 1.x 时都会问:“如果我的业务逻辑不是读取和写入怎么办?”为了更令人满意地回答这个问题,我们修改了Tasklet接口。在 Spring Batch 1.x 中,它相当平淡无奇,实际上不过是一个Callable。但在 2.0 版本中,我们赋予了它更多的灵活性,并将其更好地整合到框架的主流中(例如,核心的面向块的步骤实现现在被实现为一个TaskletTasklet


public interface Tasklet {
    ExitStatus execute(StepContribution contribution, 
        AttributeAccessor attributes);
}

这个想法是,tasklet 现在可以为封闭的步骤贡献更多,这使得它成为实现业务逻辑的一个更灵活的平台。虽然StepContribution已经是 1.x API 的一部分,但它并没有被广泛公开。它的作用是收集当前StepExecution的更新,而无需程序员担心在另一个线程中并发修改。这也告诉我们,Taskletexecute()方法将被重复调用(而不是像 1.x 框架中那样每个步骤只调用一次),因此它可以用于执行更广泛的业务处理任务。AttributeAccessor

是一个块作用域的键值对集合。tasklet 可以使用它来存储将在回滚时保留的中间结果。

Late Binding of Job and Step Attributes到目前为止(截至 M2),我们还未能利用任何新的 Spring 3.0 功能,因为还没有 Spring 3.0 的发布版本。但从下一个里程碑(M3)开始,我们将使用新的 Spring EL(表达式语言)功能来实现对还是JobParametersStepExecutionResourceProxy用于绑定到文件名作为作业的输入参数。Spring Batch 2.0 中一个更通用的解决方案是通过在适当的 Spring 作用域中定义它们来允许这些步骤属性绑定到任意组件,例如:

<bean id="step1" parent="simpleStep">
  <property name="itemReader">
    <bean class="org.springframework.batch.item.file.FlatFileItemReader" scope="step">
        <property value="#{jobParameters[inputFile]}" />
        ...
    </bean>
  </property>
  ...
</bean>

Item reader 需要绑定到一个只在运行时可用的文件名。为此,我们将其声明为 scope="step",并使用 Spring EL 绑定模式#{...}来绑定到 Job 参数。相同的模式也将适用于步骤和 Job 级别的执行上下文属性。

StepScope在 nightly snapshots 中可用,但我们仍在使用 Spring 2.5.5,因此 EL 绑定尚未生效。Step 作用域的 Bean 也是解决旧 Spring Batch 如何保持步骤线程安全问题的好方法。如果一个步骤依赖于有状态组件,如FlatFileItemReader,那么它只需要将该组件声明为 scope="step",框架就会为其创建一个惰性初始化代理,并且它将在每个步骤执行时按需创建一次。创建一个新的ApplicationContextJobExecution

来实现与 Job 的线程安全仍然是可行的,这是 1.x 中避免 Job 之间线程冲突的最佳实践。但现在 Step 作用域提供了另一种选择,而且对于大多数 Spring 用户来说,这种选择可能更容易掌握。

非顺序执行

  • 在 1.x 中,Job 的模型始终是步骤的线性序列,如果一个步骤失败,则 Job 失败。虽然许多 Job 仍然符合这种模式,并且它并未消失,但在 2.0 版本中,我们通过引入一些新功能来解除这一限制。这些功能计划在 M3 版本中发布,因此实现细节可能会发生变化,但目的是支持以下三个功能:条件执行:基于上一个步骤的ExitStatus
  • 分支到不同的步骤。这包括能够根据 FAILED 状态进行分支,这意味着步骤失败不再是 Job 的致命错误。
  • 暂停执行并等待明确的继续指令。这在存在强制手动干预来检查业务关键数据有效性的业务规则的情况下非常有用。
多个步骤的并行执行。当步骤独立时,用户可以指定哪些分支可以并行执行。截至本文撰写时,Nightly snapshots 包含一个ConditionalJob

可伸缩性

Spring Batch 1.x 一直被设计为一个可能的单 VM、多线程模型,但我们向其中添加了许多支持多进程并行执行的功能。许多项目已成功实现可扩展的解决方案,依靠 Spring Batch 的服务质量功能来确保处理仅在正确的顺序下发生。在 2.0 版本中,我们将更明确地公开这些功能。可扩展性有两种方法,我们将同时支持这两种:远程分块和分区。

远程分块

远程分块是一种将步骤的工作进行分割的技术,而无需显式了解数据的结构。任何输入源都可以通过在一个进程中读取(如 1.x 中的正常情况)并将这些项作为一个块发送到远程工作进程来动态分割。远程进程实现侦听器模式,响应请求,处理数据并发送异步回复。请求和回复的传输必须是持久的,具有保证的交付和单个消费者,并且这些功能可以通过任何 JMS 实现轻松获得。但 Spring Batch 在 Spring Integration 之上构建远程分块功能,因此它实际上与消息中间件的实际实现无关。

分区

分区是另一种方法,相比之下,它依赖于对输入数据结构的某些了解,例如主键范围或要处理的文件名。这种模型的优点是,每个分区元素的处理器可以像它们是普通 Spring Batch Job 中的单个步骤一样行事。它们不必实现任何特殊的新模式,这使得它们易于配置和测试。原则上,分区比远程分块更具可扩展性,因为它不存在从一个地方读取所有输入数据而产生的序列化瓶颈。

在 Spring Batch 2.0 中,分区由两个接口支持:PartitionHandlerStepExecutionSplitterPartitionHandler 知道执行框架——它必须将请求传输到远程步骤,并使用任何可用的网格或远程技术收集结果。 PartitionHandler 是一个 SPI,我们通过 TaskExecutor 提供了一个开箱即用的本地执行实现。这对于我们看到的一些需要对大量 IO 密集型任务进行并行处理的项目来说将是立即有用的,因为在这些情况下,远程执行只会使部署复杂化,并且不一定能在性能上提供太多帮助。其他实现将特定于执行框架,例如,某个网格提供商(IBM、Oracle、Terracotta、Appistry 等),并且我们不希望在 Spring Batch 中对其中任何一个相对于其他供应商有偏好。

SpringSource 计划推出一款企业级批处理产品,该产品将提供完整的运行时解决方案,用于分区和远程分块,以及管理和调度方面的需求。敬请关注,了解该产品的更多详细信息,或参加 Spring One Americas

配置:注解和XML命名空间

使用注解实现批处理逻辑的想法是类比 Spring @MVC。其最终效果是,您不必实现并可能注册一堆接口(Reader、Writer、Processor、Listeners 等),只需注解一个 POJO 并将其插入到步骤中。有对应于各种接口的方法级别注解,以及对应于 Job、Step 和 Chunk 级别属性的参数级别注解和工厂方法(有点像@ModelParameter还是@RequestParameter

Spring Batch 的 XML 命名空间使得常见事项的配置更加容易。例如,我们上面提到的截至本文撰写时,Nightly snapshots 包含一个ConditionalJob


<job>
  <step name="gamesLoad" next="playerLoad"/>
  <step name="playerLoad">
    <next on="*" to="summarize"/>
    <next on="FAILED" to="compensate"/>
  </step>
  <step name="compensate"/>
  <step name="summarize"/>
</job>

第一个步骤(“gamesLoad”)紧接着是“playerLoad”,但如果失败,我们可以转到替代步骤(“compensate”),而不是正常地用“summarize”步骤完成 Job。XML 中<step元素的 name= 属性是一个 Bean 引用,所以Step的实现定义在别处(可能通过注解)。

数据库 Schema 更改

在元数据 Schema 中有一些整理工作和对数据模型的扩展。我们将为从 1.x 迁移到 2.0 的任何人提供更新脚本,因此它们不应该引起任何问题,并且肯定会使导航和与元数据交互更加容易。对于刚接触 Spring Batch 的您来说,该框架的一些关键优势是服务质量功能,如可重启动性和幂等性(只处理一次业务数据)。我们通过关系数据库中的共享状态(在大多数用例中)来实现这些功能,并且此数据库中的数据模型的定义在 2.0 版本中发生了一些变化。

主要更改与JobParametersExecutionContextStepExecution的存储有关,它以前集中在一个表中,即使上下文可以与StepExecution相关联。新模型将更受 DBA 的欢迎,因为它使 DDL 中的关系更加透明。我们还开始将上下文值存储在 JSON 中,以便于人类用户阅读和跟踪(单个实体的上下文都存储在一个表的一行中,而不是多行)。

我们还添加了更多用于计数和核算已执行和已跳过的项的统计信息,将每个阶段的读取、处理和写入的总项数分开。对于不将执行分为读取、处理、写入的步骤(或 tasklet),这比需要的要多,但对于大多数用例来说,它比只存储整体项计数更合适。

结束语

希望本文能激发您对 Spring Batch 2.0 的兴趣,并且您有时间下载一个快照或里程碑版本并尝试一下。我们从社区获得了非常好的反馈,没有您的帮助和支持,该产品就不会像现在这样出色。如果我遗漏了您感兴趣的内容,或者没有提供足够的细节,我表示歉意,但我需要将其压缩成一个合理的博客大小的格式。如果有人感兴趣,我可以写更多内容——尽管问。

如果您想了解更多信息,有机会在今年的Spring One Americas上听取团队关于 Spring Batch 2.0 和所有其他 SpringSource 活动的介绍。Lucas Ward 将在一个会话中展示 Spring Batch 2.0 的功能,我将更详细地介绍可扩展性功能。您还可以查看论坛上的讨论,或者在这里访问主页 这里

获取 Spring 新闻通讯

通过 Spring 新闻通讯保持联系

订阅

领先一步

VMware 提供培训和认证,助您加速进步。

了解更多

获得支持

Tanzu Spring 提供 OpenJDK™、Spring 和 Apache Tomcat® 的支持和二进制文件,只需一份简单的订阅。

了解更多

即将举行的活动

查看 Spring 社区所有即将举行的活动。

查看所有