1. Spring Batch 介绍

企业域内的许多应用程序都需要批量处理才能在关键任务环境中执行业务操作。这些业务操作包括自动化,复杂的海量信息处理,无需用户交互即可最有效地进行处理。这些操作通常包括基于时间的事件(例如,月末计算,通知或通信),周期性应用非常大的数据集重复处理的复杂业务规则(例如,保险利益确定或费率调整)或所接收信息的集成从通常需要以事务处理方式进行格式化,验证和处理的内部和外部系统进入记录系统。批处理每天用于为企业处理数十亿笔事务。

Spring Batch 是一个轻量级的,全面的批处理框架,旨在支持开发对企业系统的日常运行至关重要的强大的批处理应用程序。 Spring Batch 构建在生产力,基于 POJO 的开发方法以及人们从 Spring 框架中了解到的通用易用性的基础之上,同时使开发人员在必要时可以轻松访问和利用更高级的企业服务。 Spring Batch 不是一个调度框架。商业和开放源代码空间(例如 Quartz,Tivoli,Control-M 等)中都有许多好的企业调度程序。它旨在与调度程序结合使用,而不是替换调度程序。

Spring Batch 提供了可重用的功能,这些功能对于处理大量记录至关重要,包括日志记录/跟踪,事务 Management,作业处理统计信息,作业重启,跳过和资源 Management。它还提供了更高级的技术服务和功能,这些功能和功能将通过优化和分区技术来实现极高容量和高性能的批处理作业。简单以及复杂的大批量批处理作业都可以以高度可扩展的方式利用框架来处理大量信息。

1.1 Background

尽管开源软件 Item 和相关社区将更多的注意力放在基于 Web 的和基于 SOA 消息传递的架构框架上,但是尽管满足了持续的处理需求,但是对于可重用的架构框架却缺乏足够的关注以适应基于 Java 的批处理需求。企业 IT 环境中的此类处理。缺乏标准的,可重复使用的批处理体系结构,导致在 Client 端企业 IT 功能内开发的许多一次性内部解决方案激增。

SpringSource 和埃森哲合作进行了更改。埃森哲(Accenture)在实现批处理体系结构方面的动手行业和技术经验,SpringSource 的深厚技术经验以及 Spring 久经验证的编程模型共同标志着自然而强大的合作伙伴关系,以创建旨在填补企业 Java 重要缺口的高质量,与市场相关的软件。两家公司目前还与许多 Client 合作,共同开发基于 Spring 的批处理体系结构解决方案,以解决类似的问题。这提供了一些有用的附加细节和现实生活中的约束条件,有助于确保解决方案可以应用于 Client 提出的现实问题。由于这些原因,SpringSource 和埃森哲(Accenture)携手合作,共同开发 Spring Batch。

埃森哲基于数十年在使用最后几代平台(例如,COBOL/Mainframe,C/Unix,现在是 Java /任何地方)构建批处理架构方面的经验,为 Spring BatchItem 贡献了以前专有的批处理架构框架。以及提交者资源,以推动支持,增强功能和 FutureRoute 图。

埃森哲与 SpringSource 的合作旨在促进软件处理方法,框架和工具的标准化,企业用户在创建批处理应用程序时可以一致地利用它们。希望为企业 IT 环境提供标准的,经过验证的解决方案的公司和政府机构将从 Spring Batch 中受益。

1.2 使用场景

典型的批处理程序通常从数据库,文件或队列中读取大量记录,以某种方式处理数据,然后以修改后的形式写回数据。 Spring Batch 自动执行此基本批处理迭代,从而提供了将一组类似的事务作为一组处理的功能,通常在脱机环境中无需任何用户交互。批处理作业是大多数 ITItem 的一部分,Spring Batch 是唯一提供可靠的企业级解决方案的开源框架。

Business Scenarios

  • 定期提交批处理

  • 并行批处理:作业的并行处理

  • 分阶段的企业消息驱动的处理

  • 大规模并行批处理

  • 失败后手动或计划重启

  • Sequences 处理相关步骤(扩展了工作流程驱动的批次)

  • 部分处理:跳过记录(例如回滚)

  • 整批事务:适用于小批量或现有存储过程/脚本的情况

Technical Objectives

  • 批处理开发人员使用 Spring 编程模型:专注于业务逻辑;让框架照顾基础架构。

  • 在基础结构,批处理执行环境和批处理应用程序之间明确分离关注点。

  • 提供通用的核心执行服务作为所有 Item 都可以实现的接口。

  • 提供可以直接使用的核心执行接口的简单和默认实现。

  • 通过在所有层中利用 spring 框架,轻松配置,定制和扩展服务。

  • 所有现有的核心服务应易于替换或扩展,而不会影响基础架构层。

  • 提供一个简单的部署模型,其架构 JAR 与使用 Maven 构建的应用程序完全分开。

1.3 Spring Batch 体系结构

Spring Batch 设计时考虑了可扩展性,并考虑了不同的最终用户群体。下图显示了分层体系结构的草图,该结构支持最终用户开发人员的可扩展性和易用性。

图 1.1:Spring Batch 分层架构

这种分层的体系结构突出了三个主要的高级组件:应用程序,核心和基础结构。该应用程序包含所有批处理作业和开发人员使用 Spring Batch 编写的自定义代码。批处理核心包含启动和控制批处理作业所需的核心运行时类。它包括诸如JobLauncherJobStep的实现。 Application 和 Core 都构建在通用基础架构之上。此基础结构包含通用的读取器和写入器,以及RetryTemplate之类的服务,应用程序开发人员(ItemReaderItemWriter)以及核心框架本身都使用它们。 (重试)

1.4 一般批处理原则和准则

以下是构建批处理解决方案时要考虑的一些关键原则,准则和一般注意事项。

  • 批处理体系结构通常会影响在线体系结构,反之亦然。在可能的情况下,请使用通用的构建块同时考虑架构和环境进行设计。

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

  • 处理数据时应尽可能靠近其物理位置(反之亦然)(即,将数据保留在进行处理的位置)。

  • 最小化系统资源的使用,尤其是 I/O。在内存中执行尽可能多的操作。

  • 查 Watch 应用程序 I/O(分析 SQL 语句)以确保避免不必要的物理 I/O。特别是,需要寻找以下四个常见缺陷:

  • 当可以一次读取数据并将其缓存或保存在工作存储器中时,为每个事务读取数据;

    • 重新读取事务的数据,其中在同一事务中较早读取了该数据;

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

    • 没有在 SQL 语句的 WHERE 子句中指定键值。

  • 请勿在批处理中两次执行操作。例如,如果出于报告目的需要数据汇总,则在初始处理数据时,如果可能,请增加存储的总数,因此报告应用程序不必重新处理相同的数据。

  • 在批处理应用程序开始时分配足够的内存,以避免在此过程中耗时的重新分配。

  • 关于数据完整性,请始终假设最坏的情况。插入足够的检查并记录验证以维护数据完整性。

  • 实施校验和以进行内部验证。例如,平面文件应具有预告片记录,以告知文件中的记录总数以及关键字段的集合。

  • 在具有实际数据量的类似生产的环境中,尽早计划和执行压力测试。

  • 在大型批处理系统中,备份可能具有挑战性,尤其是当系统以 24-7 联机联机运行时。在线设计中通常会妥善处理数据库备份,但是文件备份也应同样重要。如果系统依赖平面文件,则不仅应构建文件备份程序并形成文件,还应进行定期测试。

1.5 批处理策略

为了帮助设计和实现批处理系统,应以示例结构图和代码 Shell 的形式向设计人员和程序员提供基本的批处理应用程序构建模块和模式。在开始设计批处理作业时,应将业务逻辑分解为一系列步骤,可以使用以下标准构件来实现:

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

  • *验证应用程序:*验证应用程序可确保所有 Importing/输出记录正确且一致。验证通常基于文件头和尾标,校验和和验证算法以及记录级别的交叉检查。

  • *提取应用程序:*一个应用程序,它从数据库或 Importing 文件中读取一组记录,根据 sched 义的规则选择记录,然后将记录写入输出文件。

  • *提取/更新应用程序:*一个应用程序,它从数据库或 Importing 文件中读取记录,并根据每个 ImportingLogging 找到的数据来驱动对数据库或输出文件的更改。

  • *处理和更新应用程序:*一个应用程序,用于对来自摘录或验证应用程序的 Importing 事务进行处理。处理通常将涉及读取数据库以获得处理所需的数据,可能会更新数据库并创建记录以进行输出处理。

  • *输出/格式应用程序:*应用程序读取 Importing 文件,根据标准格式从该 Logging 重组数据,并生成输出文件以供打印或传输到另一个程序或系统。

此外,应为无法使用前面提到的构建块构建的业务逻辑提供基本的应用程序 Shell。

除主要构建块外,每个应用程序都可以使用一个或多个标准 Util 步骤,例如:

  • 排序-一种程序,该程序读取 Importing 文件并生成输出文件,其中已根据 Logging 的排序键字段对记录进行了重新排序。排序通常由标准系统 Util 执行。

  • 拆分-一种程序,它读取一个 Importing 文件,并根据字段值将每个记录写入几个输出文件之一。拆分可以由参数驱动的标准系统 Util 定制或执行。

  • 合并-一种程序,可从多个 Importing 文件中读取记录,并使用 Importing 文件中的合并数据生成一个输出文件。合并可以通过参数驱动的标准系统 Util 来定制或执行。

批处理应用程序还可以按其 Importing 源进行分类:

  • 数据库驱动的应用程序由从数据库检索的行或值驱动。

  • 文件驱动的应用程序由从文件中检索的记录或值驱动。

  • 消息驱动的应用程序由从消息队列检索的消息驱动。

任何批处理系统的基础都是处理策略。影响策略选择的因素包括:估计的批处理系统数量,与联机或其他批处理系统的并发性,可用的批处理窗口(以及更多希望以 24x7 全天候运行的企业,因此没有明显的批处理窗口)。

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

  • 脱机期间在批处理窗口中进行正常处理

  • 并行批处理/在线处理

  • 同时并行处理许多不同的批生产或作业

  • 分区(即同时处理同一作业的许多实例)

  • 这些的结合

上面列表中的 Sequences 反映了实现的复杂性,在批处理窗口中的处理最容易实现,而对分区的实现最复杂。

商业调度程序可能会支持其中一些或全部选项。

在以下部分中,将详细讨论这些处理选项。重要的是要注意,批处理过程采用的提交和锁定策略将取决于执行的处理类型,并且根据经验,在线锁定策略也应使用相同的原理。因此,在设计整体体系结构时,批处理体系结构不能仅是事后的想法。

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

1.批处理窗口中的正常处理 对于在单独的批处理窗口中运行的简单批处理过程,在线用户或其他批处理过程不需要更新数据,并发不是问题,可以在以下位置进行一次提交批处理运行结束。

在大多数情况下,更健壮的方法更为合适。要记住的是,批处理系统会随着时间的流逝而增长,无论是从复杂性还是要处理的数据量来 Watch。如果没有锁定策略,并且系统仍依赖单个提交点,则修改批处理程序可能会很麻烦。因此,即使使用最简单的批处理系统,也要考虑需要重新启动-恢复选项的提交逻辑以及有关以下更复杂情况的信息。

2.并行批处理/在线处理 处理在线用户可以同时更新的数据的批处理应用程序,不应锁定在线用户可能需要的任何数据(数据库或文件中的数据)几秒钟。同样,在每笔事务结束时,更新也应提交给数据库。这样可以将其他进程不可用的数据部分和数据不可用的经过时间最小化。

最小化物理锁定的另一种方法是使用乐观锁定模式或悲观锁定模式来实现逻辑行级锁定。

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

  • 悲观锁定是任何假定记录争用可能性很高的锁定策略,因此需要在检索时获得物理或逻辑锁定。一种悲观逻辑锁定使用数据库表中的专用锁定列。当应用程序检索要更新的行时,它将在锁列中设置一个标志。有了该标志,其他尝试检索同一行的应用程序在逻辑上将失败。当设置标志的应用程序更新该行时,它还会清除该标志,从而使该行可以被其他应用程序检索。请注意,在初始获取和设置标志之间还必须保持数据的完整性,例如通过使用 db 锁(例如 SELECT FOR UPDATE)。还要注意,该方法与物理锁定有相同的缺点,除了 Management 超时机制要容易一些,该机制可以在锁定记录的情况下在用户午餐时释放锁定。

这些模式不一定适用于批处理,但可以用于并发批处理和联机处理(例如,在数据库不支持行级锁定的情况下)。通常,乐观锁定更适合于在线应用程序,而悲观锁定更适合于批处理应用程序。每当使用逻辑锁定时,必须对访问逻辑锁定保护的数据实体的所有应用程序使用相同的方案。

请注意,这两种解决方案都只解决锁定单个记录的问题。通常,我们可能需要锁定逻辑上相关的记录组。使用物理锁,您必须非常仔细地 Management 这些锁,以避免潜在的死锁。使用逻辑锁,通常最好构建一个逻辑锁 Management 器,该 Management 器了解要保护的逻辑记录组,并可以确保锁是连贯的和非死锁的。此逻辑锁 Management 器通常使用自己的表进行锁 Management,争用报告,超时机制等。

3.并行处理 并行处理允许多个批处理运行/作业并行运行,以最大程度地减少总的批处理处理时间。只要作业不共享相同的文件,数据库表或索引空间,就没有问题。如果这样做,则应使用分区数据来实现此服务。另一种选择是构建一个架构模块,以使用控制表来维护相互依赖性。控制表应为每个共享资源及其是否由应用程序使用而包含一行。然后,批处理体系结构或并行作业中的应用程序将从该表中检索信息,以确定它是否可以访问所需的资源。

如果数据访问没有问题,则可以通过使用其他线程进行并行处理来实现并行处理。在大型机环境中,传统上使用并行作业类,以确保所有进程有足够的 CPU 时间。无论如何,该解决方案必须足够强大以确保所有正在运行的进程的时间片。

并行处理中的其他关键问题包括负载平衡和常规系统资源(例如文件,数据库缓冲池等)的可用性。还请注意,控制表本身很容易成为关键资源。

4.分区 使用分区允许大型批处理应用程序的多个版本同时运行。这样做的目的是减少处理长批处理作业所需的时间。可以成功分区的进程是可以拆分 Importing 文件和/或对主数据库表进行分区以允许应用程序针对不同的数据集运行的进程。

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

图 1.2:分区过程

该架构应足够灵活,以允许动态配置分区数量。自动配置和用户控制配置均应考虑。自动配置可以基于诸如 Importing 文件大小和/或 Importing 记录数之类的参数。

4.1 分区方法 以下列出了一些可能的分区方法。选择分区方法必须根据具体情况进行。

  • 1.记录集的固定和均匀分解*

这涉及将 Importing 记录集分成偶数个部分(例如 10 个,其中每个部分恰好占整个记录集的 1/10)。然后由批处理/提取应用程序的一个实例处理每个部分。

为了使用此方法,将需要进行预处理以拆分记录集。拆分的结果将是一个上下限放置数,可以将其用作批处理/提取应用程序的 Importing,以便将其处理仅限于其部分。

预处理可能会产生很大的开销,因为它必须计算并确定记录集每个部分的界限。

  • 2.按关键列细分*

这涉及通过键列(例如位置代码)分解 Importing 记录集,并将每个键的数据分配给批处理实例。为了实现这一点,列值可以是

  • 3.通过分区表分配给批处理实例(有关详细信息,请参见下文).*

  • 4.通过部分值(例如值 0000-0999、1000-1999 等)分配给批处理实例*

在选项 1 下,添加新值将意味着手动重新配置批次/提取,以确保将新值添加到特定实例。

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

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

  • 5.按观 Watch 次数细分*

这种方法基本上是按键列拆分的,但是在数据库级别。它涉及将记录集分解为视图。批处理应用程序的每个实例在处理过程中将使用这些视图。分解将通过对数据进行分组来完成。

使用此选项,必须将批处理应用程序的每个实例配置为命中特定视图(而不是主表)。同样,随着新数据值的添加,该新数据组将必须包含在视图中。没有动态配置功能,因为实例数量的更改将导致视图的更改。

  • 6.添加加工 Metrics*

这涉及在 Importing 表中添加一个新列,该列用作指示符。作为预处理步骤,所有 Metrics 都将标记为未处理。在批处理应用程序的记录获取阶段,将以该记录被标记为未处理的条件来读取记录,并且一旦读取(带锁)它们便被标记为正在处理。该记录完成后,指示符将更新为完成或错误。批处理应用程序的许多实例无需更改即可启动,因为附加列可确保记录仅处理一次。

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

  • 7.将表提取到平面文件*

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

使用此选项,将表提取到文件中并进行拆分的额外开销可能会抵消多分区的影响。通过更改文件分割脚本可以实现动态配置。

  • 8.哈希列的使用*

此方案涉及在用于检索驱动程序记录的数据库表中添加哈希列(键/索引)。该哈希列将具有指示符,用于确定批处理应用程序的哪个实例将处理此特定行。例如,如果有三个批处理实例要启动,则指示符“ A”将标记该行以供实例 1 处理,指示符“ B”将标记该行以供实例 2 处理,以此类推。

然后,用于检索记录的过程将具有一个附加的 WHERE 子句,以选择由特定指示符标记的所有行。此表中的插入内容将涉及添加标记字段,该字段默认为实例之一(例如'A')。

一个简单的批处理应用程序将用于更新 Metrics,例如在不同实例之间重新分配负载。添加足够多的新行后,可以运行该批处理(除批处理窗口外,任何时候均可)将新行重新分配给其他实例。

批处理应用程序的其他实例仅需要运行上述批处理应用程序即可重新分配指示符,以适应新的实例数量。

4.2 数据库和应用程序设计原则

支持使用键列方法针对分区数据库表运行的多分区应用程序的体系结构应包括用于存储分区参数的中央分区存储库。这提供了灵 Active 并确保了可维护性。该存储库通常由一个称为分区表的表组成。

存储在分区表中的信息将是静态的,并且通常应由 DBA 维护。该表应包含一个信息行,用于多分区应用程序的每个分区。该表应包含以下列:程序 ID 代码,分区号(分区的逻辑 ID),该分区的 db 键列的低值,此分区的 db 键列的高值。

在程序启动时,应将程序 ID 和分区号从体系结构传递到应用程序(控制处理任务集)。这些变量用于读取分区表,以确定应用程序要处理的数据范围(如果使用键列方法)。此外,在整个处理过程中必须使用分区号以:

  • 添加到输出文件/数据库更新中以使合并过程正常运行

  • 将正常处理报告给批处理日志,并将在执行过程中发生的任何错误报告给体系结构错误处理程序

4.3 最小化死锁

当应用程序并行或分区运行时,数据库资源争用和死锁可能发生。至关重要的是,数据库设计团队应尽可能消除潜在的争用情况,这是数据库设计的一部分。

还要确保数据库索引表在设计时考虑了防止死锁和性能。

死锁或热点通常发生在 Management 或体系结构表中,例如日志表,控制表和锁定表。还应考虑这些含义。实际的压力测试对于确定体系结构中的可能瓶颈至关重要。

为了最大程度地减少冲突对数据的影响,体系结构应在连接到数据库或遇到死锁时提供诸如 await 和重试间隔之类的服务。这意味着内置机制可以对某些数据库返回码作出反应,而不是立即发出错误处理,而是 awaitsched 时间并重试数据库操作。

4.4 参数传递和验证

分区体系结构对于应用程序开发人员应该相对透明。该体系结构应执行与在分区模式下运行应用程序相关的所有任务,包括:

  • 在应用程序启动之前检索分区参数

  • 在应用程序启动之前验证分区参数

  • 在启动时将参数传递给应用程序

验证应包括检查以确保:

  • 该应用程序具有足够的分区来覆盖整个数据范围

  • 分区之间没有间隙

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

此外,体系结构还应考虑分区的合并。关键问题包括:

  • 在进入下一个作业步骤之前,是否必须完成所有分区?

  • 如果其中一个分区中止会怎样?