1. Spring Batch 简介

企业 domain 中的许多应用程序需要批量处理才能在关键任务环境中执行业务操作。这些业务操作包括大量信息的自动化,复杂处理,无需用户交互即可最有效地处理这些信息。这些操作通常包括基于 time 的 events(e.g. month-end 计算,通知或通信),在非常大的数据集中重复处理的复杂业务规则的周期性应用(e.g. 保险利益确定或费率调整),或者收到的信息的整合来自内部和外部系统,通常需要以 transactional 方式格式化,验证和处理 record 系统。批处理用于每天为企业处理数十亿的 transactions。

Spring Batch 是一个轻量级,全面的批量 framework,旨在开发对企业系统日常运营至关重要的强大批量应用程序。 Spring Batch 建立在人们从 Spring Framework 中了解的生产力,POJO-based 开发方法和一般易用性的基础上,同时使开发人员可以在必要时轻松访问和利用更高级的企业服务。 Spring Batch 不是一个调度 framework。商业和开源空间(例如 Quartz,Tivoli,Control-M 等)都有许多良好的企业调度程序。它旨在与调度程序一起使用,而不是替换调度程序。

Spring Batch 提供了可重复使用的函数,这些函数对于处理大量记录至关重要,包括 logging/tracing,transaction management,job 处理统计信息,job restart,skip 和 resource management。它还提供了更多先进的技术服务和 features,通过优化和分区技术,可以实现极高 high-volume 和高性能的批处理作业。简单和复杂的 high-volume 批处理作业可以以高度可扩展的方式利用 framework 来处理大量信息。

1.1 背景

虽然开源软件项目和相关社区更加关注 web-based 和 SOA messaging-based 架构框架,但是尽管需要在企业 IT 环境中持续处理此类处理,但仍然缺乏对可重用 architecture 框架的关注以适应 Java-based 批处理需求。 。缺乏标准的,可重复使用的批处理 architecture 导致了 client 企业 IT 功能中开发的许多 one-off,in-house 解决方案的激增。

SpringSource 和埃森哲合作改变了这一点。埃森哲在实施批量架构方面的行业和技术经验,SpringSource 的深度技术经验以及 Spring 经过验证的编程模式共同标志着一种自然而强大的合作伙伴关系,旨在创建旨在填补企业 Java 重要空白的市场相关软件。两家公司目前还在与一些解决类似问题的客户合作 - 开发 Spring-based 批 architecture 解决方案。这提供了一些有用的额外细节和 real-life 约束,有助于确保解决方案可以应用于 clients 提出的 real-world 问题。出于这些原因以及更多原因,SpringSource 和埃森哲已经合作开发 Spring Batch。

埃森哲提供了以前专有的批处理架构框架,基于数个批量架构的几十年经验,与最后几代平台(i.e.,COBOL/Mainframe,C /Unix,现在 Java/anywhere)一起到 Spring Batch 项目以及提交者资源推动支持,增强和未来路线图。

埃森哲与 SpringSource 之间的合作旨在促进软件处理方法,框架和工具的标准化,这些方法,框架和工具可以在创建批处理应用程序时由企业用户持续使用。希望为其企业 IT 环境提供标准的,经过验证的解决方案的公司和政府机构将受益于 Spring Batch。

1.2 使用场景

典型的批处理程序通常从数据库,文件或队列中读取大量记录,以某种方式处理数据,然后以修改的形式写回数据。 Spring Batch 自动执行此基本批处理迭代,提供了将类似 transactions 作为一个集处理的功能,通常在离线环境中,无需任何用户交互。批处理作业是大多数 IT 项目的一部分,Spring Batch 是唯一提供强大的 enterprise-scale 解决方案的开源 framework。

业务场景

  • 定期提交批处理 process

  • 并发批处理:_并行处理 job

  • 分阶段,企业 message-driven 处理

  • 大规模并行批处理

  • 失败后手动或预定重启

  • 依赖步骤的顺序处理(使用 extensions 到 workflow-driven 批次)

  • 部分处理:跳过记录(回滚时 e.g. )

  • Whole-batch transaction:适用于批量较小或存在的情况 procedures/scripts

技术目标

  • 批处理开发人员使用 Spring 编程 model:专注于业务逻辑;让 framework 照顾基础设施。

  • 清楚地分离基础架构,批处理执行环境和批处理应用程序之间的关注点。

  • 提供 common 核心执行服务作为所有项目都可以实现的接口。

  • 提供可以“开箱即用”使用的核心执行接口的简单和默认 implementation。

  • 通过在所有层中利用 spring framework,轻松配置,自定义和扩展服务。

  • 所有现有核心服务都应易于更换或扩展,而不会对基础架构层产生任何影响。

  • 提供一个简单的部署 model,architecture JAR 与使用 Maven 构建的 application 完全分开。

1.3 Spring Batch Architecture

Spring Batch 的设计具有可扩展性和多样化的最终用户组。下图显示了分层 architecture 的草图,它支持 end-user 开发人员的可扩展性和易用性。

图 1.1:Spring Batch Layered Architecture

这种分层的 architecture 突出了三个主要的高级 level 组件:Application,Core 和 Infrastructure。 application 包含开发人员使用 Spring Batch 编写的所有批处理作业和自定义 code。 Batch Core 包含启动和控制批处理 job 所需的核心运行时 classes。它包括诸如JobLauncherJobStep __mplementations 之类的东西。 Application 和 Core 都构建在 common 基础架构之上。此基础结构包含 common readers 和 writers,以及RetryTemplate等服务,application 开发人员(ItemReaderItemWriter)和核心 framework 本身都使用这些服务。 (重试)

1.4 一般批次原则和指南

以下是 building 批处理解决方案时要考虑的一些 key 原则,指南和一般注意事项。

  • 批 architecture 通常影响 on-line architecture,反之亦然。尽可能使用 common building 块来设计体系结构和环境。

  • 尽可能简化并避免在单批应用程序中构建复杂的逻辑结构。

  • 尽可能接近数据物理驻留的位置处理数据,反之亦然(i.e.,将数据保存在处理过程中)。

  • 最大限度地减少系统资源的使用,尤其是 I/O。在内部 memory 中执行尽可能多的操作。

  • 查看 application I/O(分析 SQL statements)以确保避免不必要的物理 I/O。特别是,需要寻找以下四个 common 缺陷:

  • 当数据可以被读取一次并保持缓存或在工作存储中时,读取每个 transaction 的数据;

  • 重新读取 transaction 的数据,其中数据在同一 transaction 中先前读取;

  • 导致不必要的 table 或索引扫描;

  • 未在 SQL 语句的 WHERE 子句中指定 key 值。

  • 不要在批处理中运行两次 run。例如,如果您需要用于报告目的的数据汇总,则在最初处理数据时尽可能增加存储的总计,因此您的报告 application 不必重新处理相同的数据。

  • 在批处理 application 的开头分配足够的 memory,以避免 process 期间的 time-consuming 重新分配。

  • 总是假设数据完整性最差。 插入适当的检查和 record 验证以保持数据完整性。

  • 尽可能实现内部验证的校验和。对于 example,flat files 应该有一个预告片 record,告诉文件中的记录总数和 key 字段的集合。

  • 在具有真实数据量的 production-like 环境中尽可能早地计划和执行压力测试。

  • 在大批量系统中,备份可能具有挑战性,特别是如果系统在 24-7 基础上与 on-line 并行运行。数据库备份通常在 on-line 设计中得到很好的处理,但文件备份应该被视为同样重要。如果系统依赖于 flat files,则文件备份过程不仅应该到位并记录在案,而且应该定期进行测试。

1.5 批处理策略

为了帮助设计和实现批处理系统,应该以 sample 结构图和 code shell 的形式向设计人员和程序员提供基本的批处理 application building 块和模式。在开始设计批处理 job 时,应将业务逻辑分解为一系列步骤,这些步骤可以使用以下标准 building 块来实现:

  • 转换 Applications:对于由外部系统提供或生成的每种类型的文件,都需要创建转换应用程序,以将提供的 transaction 记录转换为处理所需的标准格式。这种类型的批处理应用程序可以部分或全部由翻译实用程序模块组成(请参阅基本批处理服务)。

  • 验证 Applications:验证 applications 确保所有 input/output 记录都正确且一致。验证通常基于文件 headers 和预告片,校验和和验证算法以及 record level cross-checks。

  • 提取 Applications:一个 application,它从数据库或输入文件中读取一组记录,根据预定义的规则选择记录,并将记录写入输出文件。

  • Extract/Update Applications:一个 application,它从数据库或输入文件中读取记录,并对由每个输入 record 中的数据驱动的数据库或输出文件进行更改。

  • 处理和更新 Applications:一个 application,用于从提取或验证 application 对输入 transactions 执行处理。处理通常涉及读取数据库以获取处理所需的数据,可能更新数据库和创建记录以进行输出处理。

  • Output/Format Applications:Applications 读取输入文件,根据标准格式重新构造来自此 record 的数据,并生成用于打印或传输到另一个程序或系统的输出文件。

此外,应为业务逻辑提供基本的 application shell,这些逻辑无法使用前面提到的 building 块构建。

除了主 building 块之外,每个 application 都可以使用一个或多个标准实用程序步骤,例如:

  • 排序 - 一个程序,它读取输入文件并根据记录中的排序 key 字段生成输出文件,其中记录已 re-sequenced。排序通常由标准系统实用程序执行。

  • 拆分 - 读取单个输入文件的程序,并根据字段 value 将每个 record 写入多个输出 files 之一。拆分可以由 parameter-driven 标准系统实用程序定制或执行。

  • 合并 - 一个程序,它从多个输入 files 读取记录,并生成一个输出文件,其中包含来自输入 files 的组合数据。合并可以由 parameter-driven 标准系统实用程序定制或执行。

批量应用程序还可以按其输入源进行分类:

  • Database-driven applications 由从数据库中检索的行或值驱动。

  • File-driven applications 由从文件中检索的记录或值驱动。

  • Message-driven applications 由从消息队列中检索的消息驱动。

任何批处理系统的基础都是处理策略。影响策略选择的因素包括:估计的批处理系统容量,与 on-line 或其他批处理系统的并发性,可用的批处理 windows(以及更多企业希望启动和运行 24x7,这不会留下明显的批处理 windows)。

批处理的典型处理选项是:

  • off-line 期间批处理窗口中的正常处理

  • 并发批处理/ on-line 处理

  • 在同一 time 处并行处理许多不同的批处理运行或作业

  • 分区(i.e.在同一 time 处理同一 job 的许多实例)

  • 这些的组合

上面列表中的 order 反映了 implementation 复杂性,批处理窗口中的处理是最简单的,并且分区是最复杂的实现。

商业调度程序可以支持部分或全部这些选项。

在下一节中,将更详细地讨论这些处理选项。重要的是要注意批处理采用的提交和锁定策略将取决于执行的处理类型,并且根据经验,on-line 锁定策略也应该使用相同的原则。因此,在设计整体 architecture 时,批处理 architecture 不能仅仅是事后的想法。

锁定策略只能使用普通数据库锁,或者可以在 architecture 中实现其他自定义锁定服务。锁定服务将跟踪数据库锁定(例如,通过将必要信息存储在专用的 db-table 中),并为请求数据库操作的 application 程序提供或拒绝权限。此 architecture 也可以实现重试逻辑,以避免在发生锁定情况时中止批处理 job。

1.批处理窗口中的正常处理对于简单的批处理进程在单独的批处理窗口中运行,on-line 用户或其他批处理过程不需要更新的数据,并发性不是问题,最后可以完成一次提交批处理 run。

在大多数情况下,更健全的方法更合适。需要记住的一点是,批量系统随着时间的推移而增长,无论是在复杂性还是在处理数据量方面。如果没有锁定策略并且系统仍然依赖于单个提交点,则修改批处理程序可能会很痛苦。因此,即使使用最简单的批处理系统,也需要考虑 restart-recovery 选项的提交逻辑以及有关下面更复杂情况的信息。

2.并发批处理/ on-line 处理批处理应用程序处理可以由 on-line 用户同时更新的数据,不应该锁定 on-line 用户可能需要的任何数据(在数据库中或 files 中)超过几秒钟。还应在每几个 transaction 结束时将更新提交到数据库。这最小化了其他进程不可用的数据部分以及数据不可用的已用时间。

最小化物理锁定的另一个选项是使用乐观锁定 Pattern 或悲观锁定 Pattern 实现逻辑 row-level 锁定。

  • 乐观锁定假设 record 争用的可能性很小。它通常意味着在批处理和 on-line 处理同时使用的每个数据库 table 中插入时间戳列。当 application 获取一行进行处理时,它还会获取时间戳。当 application 然后尝试更新已处理的行时,更新将使用 WHERE 子句中的原始时间戳。如果时间戳匹配,则数据和时间戳将成功更新。如果时间戳不匹配,则表示另一个 application 在获取和更新尝试之间更新了同一行,因此无法执行更新。

  • 悲观锁定是任何锁定策略,假设 record 争用的可能性很高,因此需要在检索 time 时获得物理锁或逻辑锁。一种悲观的逻辑锁定使用数据库 table 中的专用 lock-column。当 application 检索更新行时,它会在锁定列中设置 flag。在 flag 到位的情况下,尝试检索同一行的其他 applications 将在逻辑上失败。当设置 flag 的 application 更新行时,它也会清除 flag,使其他 applications 可以检索该行。请注意,必须在初始提取和 flag 设置之间保持数据的完整性,例如使用数据库锁(e.g. ,SELECT FOR UPDATE)。另请注意,此方法的缺点与物理锁定相同,除了管理 building 机制更容易,如果用户在 record 被锁定时去吃午餐,将会释放锁定。

这些模式不一定适合批处理,但它们可能用于并发批处理和 on-line 处理(e.g. 在数据库不支持 row-level 锁定的情况下)。作为一般规则,乐观锁定更适合 on-line applications,而悲观锁定更适合批量应用。每当使用逻辑锁定时,必须对访问受逻辑锁保护的数据实体的所有 applications 使用相同的 scheme。

请注意,这两种解决方案仅解决锁定单个 record 的问题。通常我们可能需要锁定逻辑相关的组记录。使用物理锁,您必须非常仔细地管理这些,以避免潜在的死锁。对于逻辑锁,通常最好 build 一个逻辑锁 manager,它理解你想要保护的逻辑 record 组,并确保锁是连贯的 non-deadlocking。这个逻辑锁 manager 通常使用自己的表来进行锁管理,争用报告,time-out 机制等。

3.Parallel Processing Parallel 处理允许多个批处理运行/作业在 parallel 中运行,以最小化批处理总时间 time。这不是 long 的问题,因为作业不共享相同的 files,db-tables 或索引空格。如果他们这样做,则应使用分区数据实现此服务。另一种选择是 build architecture 模块,以使用 control table 维护相互依赖性。控件 table 应该包含每个共享资源的一行,以及它是否正由 application 使用。然后,parallel job 中的批处理 architecture 或 application 将从该 table 中检索信息,以确定它是否可以访问它所需的资源。

如果数据访问不是问题,可以通过在 parallel 中使用 process 的其他线程来实现 parallel 处理。在大型机环境中,传统上使用 parallel job classes,在 order 中确保所有进程都有足够的 CPU time。无论如何,该解决方案必须足够强大,以确保所有 running 进程的 time slice。

parallel 处理中的其他 key 问题包括负载平衡和一般系统资源(如 files,数据库缓冲池等)的可用性。另请注意,control table 本身很容易成为关键资源。

4.分区使用分区允许多个版本的大批量应用程序同时运行。这样做的目的是减少 process long 批处理作业所需的已用 time 时间。可以成功分区的进程是可以分割输入文件的那些进程 and/or 分区的主数据库表,以允许 application 对不同的数据集运行。

此外,必须将分区的进程设计为仅处理其分配的数据集。分区 architecture 必须与数据库设计和数据库分区策略紧密相关。请注意,数据库分区并不一定意味着数据库的物理分区,尽管在大多数情况下这是可取的。下图说明了分区方法:

图 1.2:分区 Process

architecture 应该足够灵活,以允许动态配置分区数。应考虑自动和用户控制的 configuration。自动 configuration 可以基于参数,例如输入文件大小 and/or 输入记录的数量。

4.1 分区方法以下列出了一些可能的分区方法。选择分区方法必须在 case-by-case 基础上完成。

  1. Record Set 的固定和偶数 Break-Up

这涉及将输入 record 集合分成偶数个部分(e.g.10,其中每个部分将具有整个 record 集合的 1/10th)。然后,每个部分由 batch/extract application 的一个实例处理。

在 order 中使用此方法,将需要预处理来拆分记录集。此拆分的结果将是一个下限和上限放置编号,可用作 order 中 batch/extract application 的输入,以将其处理限制为单独的部分。

预处理可能是一个很大的开销,因为它必须计算和确定 record 集的每个部分的边界。

  1. 由 Key 列分解

这涉及拆分 key 列设置的输入 record,例如 location code,并将每个 key 的数据分配给批处理实例。在 order 中实现这一点,列值可以是

  1. 通过分区 table 分配给批处理实例(有关详细信息,请参见下文)。

  2. 通过 value 的一部分(e.g. values 0000-0999,1000 - 1999,etc.))分配给批处理实例

在选项 1 下,添加新值意味着手动重新配置 batch/extract 以确保将新的 value 添加到特定实例。

在选项 2 下,这将确保通过批 job 的实例覆盖所有值。但是,一个实例处理的值的数量取决于列值的分布(i.e.在 0000-0999 范围内可能存在大量位置,而在 1000-1999 范围内可能很少)。在此选项下,数据范围应设计为考虑分区。

在这两个选项下,无法实现记录到批处理实例的最佳均匀分布。没有动态配置使用的批处理实例数。

  1. 按意见分解

这种方法基本上是由 key 列分解,而是在数据库 level 上。它涉及将记录集分解为视图。这些视图将在处理期间由批处理 application 的每个实例使用。分解将通过分组数据来完成。

使用此选项,必须将批处理 application 的每个实例配置为命中特定视图(而不是 master table)。此外,通过添加新数据值,这个新的数据组将必须包含在视图中。没有动态配置功能,因为实例数量的更改将导致视图发生更改。

  1. 添加处理指标

这涉及向输入 table 添加新列,该列充当指示符。作为预处理 step,所有指标都将标记为 non-processed。在批处理 application 的 record fetch 阶段,读取记录的条件是 record 被标记为 non-processed,一旦读取它们(带锁定),它们就被标记为处理。完成该 record 后,指示符将更新为完成或错误。批处理 application 的许多实例可以在没有更改的情况下启动,因为附加列确保 record 仅处理一次。

使用此选项,table 上的 I/O 会动态增加。在更新批处理应用程序的情况下,这种影响会减少,因为无论如何都必须进行写入。

  1. 将 Table 解压缩到平面文件

这涉及将 table 提取到文件中。然后,可以将此文件拆分为多个段,并将其用作批处理实例的输入。

使用此选项,将 table 解压缩到文件中并将其拆分的额外开销可能会抵消 multi-partitioning 的影响。可以通过更改文件拆分脚本来实现动态配置。

  1. 使用哈希列

此 scheme 涉及向用于检索驱动程序 record 的数据库表添加哈希列(key/index)。此哈希列将有一个指示器,用于确定批处理 application 的哪个实例将处理此特定行。例如,如果要启动三个批处理实例,则指示符'A'将标记该行以供实例 1 处理,指示符'B'将标记该行以供实例 2 处理,等等。

然后,用于检索记录的过程将有一个额外的 WHERE 子句来选择由特定指示符标记的所有行。此 table 中的插入将涉及添加标记字段,该字段将默认为其中一个实例(e.g. 'A')。

一个简单的批处理 application 将用于更新指标,例如在不同实例之间重新分配负载。当添加了足够多的新行时,此批处理可以 run(任何时候,批处理窗口除外)将新行重新分配给其他实例。

批处理 application 的其他实例仅需要如上所述 running 批处理 application 来重新分配指标以满足新的实例数量。

4.2 数据库和 Application 设计原则

支持使用 key 列方法运行分区数据库表的 multi-partitioned applications 的 architecture 应该包含一个用于存储分区参数的中央分区 repository。这提供了灵活性并确保了可维护性。 repository 通常由一个称为 partition table 的 table 组成。

存储在分区 table 中的信息将是静态的,通常应由 DBA 维护。 table 应该包含 multi-partitioned application 的每个分区的一行信息。 table 应具有以下列:Program ID Code,Partition Number(分区的逻辑 ID),此分区的 db key 列的低 Value,此分区的 db key 列的高 Value。

在程序 start-up 上,程序 ID 和分区号应该从 architecture(控制处理任务组)传递给 application。这些变量用于读取分区 table,以确定 application 对 process 的数据范围(如果使用了 key 列方法)。此外,在整个处理过程中必须使用分区号:

  • 在 order 中添加输出 files/database 更新,以使 merge process 正常工作

  • 报告批处理 log 的正常处理以及执行期间发生到 architecture 错误处理程序的任何错误

4.3 尽量减少僵局

当 applications run 在 parallel 或 partitioned 中时,可能会发生数据库资源争用和死锁。作为数据库设计的一部分,数据库设计团队尽可能消除潜在的争用情况至关重要。

还要确保数据库索引表的设计时考虑到死锁和 performance。

管理或 architecture 表(例如 log 表,控制表和锁定表)中经常会出现死锁或热点。还应考虑到这些问题的影响。现实的压力测试对于确定 architecture 中可能存在的瓶颈至关重要。

为了最大限度地减少冲突对数据的影响,architecture 在附加到数据库或遇到死锁时应提供 wait-and-retry 间隔等服务。这意味着 built-in 机制对某些数据库 return 代码做出反应,而不是立即发出错误处理,等待预定数量的 time 并重试数据库操作。

4.4 参数传递和验证

分区 architecture 应该对 application 开发人员相对透明。 architecture 应执行与以分区模式运行 application 相关的所有任务,包括:

  • 在 application start-up 之前检索分区参数

  • 在 application start-up 之前验证分区参数

  • 在 start-up 将参数传递给 application

验证应包括检查以确保:

  • application 有足够的分区来覆盖整个数据范围

  • 分区之间没有间隙

如果数据库已分区,则可能需要进行一些额外的验证,以确保单个分区不跨越数据库分区。

architecture 也应该考虑分区的合并。 关键问题包括:

  • 在进入下一个 job step 之前,是否必须完成所有分区?

  • 如果其中一个分区中止会发生什么?

Updated at: 9 months ago
Table of content2. Spring Batch 3.0 的新功能