1. Spring Batch 介绍

企业域内的许多应用程序都需要批量处理才能在关键任务环境中执行业务操作。这些业务包括:

  • 自动,复杂地处理大量信息,无需用户交互即可最有效地进行处理。这些操作通常包括基于时间的事件(例如月末计算,通知或通信)。

  • 定期应用非常大的数据集(例如,保险利益确定或费率调整)重复处理复杂的业务规则。

  • 从内部和外部系统接收的信息的集成,通常需要格式化,验证和以事务方式进行的处理到记录系统中。批处理每天用于为企业处理数十亿笔事务。

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

Spring Batch 提供了可重用的功能,这些功能对于处理大量记录至关重要,包括日志记录/跟踪,事务 Management,作业处理统计信息,作业重启,跳过和资源 Management。它还提供了更高级的技术服务和功能,这些功能可通过优化和分区技术来实现超大量和高性能的批处理作业。 Spring Batch 可用于简单的用例(例如,将文件读入数据库或运行存储过程),也可以用于复杂的大量用例(例如,在数据库之间移动大量数据,对其进行转换等)。上)。大量批处理作业可以以高度可扩展的方式利用框架来处理大量信息。

1.1. Background

尽管开源软件 Item 和相关社区将更多的注意力集中在基于 Web 和基于微服务的体系结构框架上,但是仍然存在着对可重用体系结构框架的关注,以适应基于 Java 的批处理需求,尽管仍然需要 continue 处理此类问题。在企业 IT 环境中进行处理。缺乏标准的,可重复使用的批处理体系结构,导致在 Client 端企业 IT 功能内开发的许多一次性内部解决方案激增。

SpringSource(现为 Pivotal)和埃森哲合作改变了这一点。埃森哲在实现批处理体系结构方面的动手行业和技术经验,SpringSource 的丰富技术经验以及 Spring 久经考验的编程模型共同构成了一种自然而强大的合作伙伴关系,以创建旨在填补企业 Java 重要缺口的高质量,与市场相关的软件。两家公司都与许多 Client 合作,他们通过开发基于 Spring 的批处理体系结构解决方案来解决类似的问题。这提供了一些有用的附加细节和现实生活中的约束条件,有助于确保解决方案可以应用于 Client 提出的现实问题。

埃森哲为 Spring Batch Item 贡献了以前专有的批处理架构框架,以及用于支持,增强功能和现有功能集的提交者资源。埃森哲的贡献基于数十年来在使用最后几代平台构建批处理架构方面的经验:COBOL/Mainframe,C/Unix,以及现在的 Java/anywhere。

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

1.2. 使用场景

典型的批处理程序通常:

  • 从数据库,文件或队列中读取大量记录。

  • 以某种方式处理数据。

  • 以修改后的形式写回数据。

Spring Batch 自动执行此基本批处理迭代,从而提供了将一组类似的事务作为一组处理的功能,通常在脱机环境中无需任何用户交互。批处理作业是大多数 IT Item 的一部分,Spring Batch 是唯一提供可靠的企业级解决方案的开源框架。

Business Scenarios

  • 定期提交批处理

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

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

  • 大规模并行批处理

  • 失败后手动或计划重启

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

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

  • 整批处理,适用于小批处理或现有存储过程/脚本的情况

Technical Objectives

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

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

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

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

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

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

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

1.3. Spring Batch 架构

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

图  1.1:Spring Batch 分层架构

图 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 全天候运行,清晰的批处理窗口正在消失)。

批处理的典型处理选项是(按实现复杂度的升序排列):

  • 脱机模式下批处理窗口中的正常处理。

  • 并发批处理或联机处理。

  • 同时并行处理许多不同的批处理运行或作业。

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

  • 前述选项的组合。

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

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

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

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

在大多数情况下,更健壮的方法更为合适。请记住,批处理系统在复杂性和它们处理的数据量方面都有随时间增长的趋势。如果没有锁定策略,并且系统仍依赖单个提交点,则修改批处理程序可能会很麻烦。因此,即使使用最简单的批处理系统,也要考虑对重新启动-恢复选项的提交逻辑的需求,以及有关本节稍后介绍的更复杂情况的信息。

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

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

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

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

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

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

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

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

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

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

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

图  1.2:分区过程

图 2.分区过程

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

4.1 分区方法 必须根据具体情况选择分区方法。以下列表描述了一些可能的分区方法:

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

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

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

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

  • 2.按关键列细分*

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

  • 由分区表分配给批处理实例(本节后面将介绍)。

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

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

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

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

  • 3.按观 Watch 次数细分*

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

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

  • 4.添加加工 Metrics*

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

使用此选项,表上的 I/O 会动态增加。在更新批处理应用程序的情况下,由于必须进行写操作,因此减少了这种影响。

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

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

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

  • 6.哈希列的使用*

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

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

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

批处理应用程序的其他实例仅需要运行如前几段所述的批处理应用程序,即可重新分配指示符以与新数量的实例一起使用。

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

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

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

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

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

  • 将正常处理报告给批处理日志,并将任何错误报告给体系结构错误处理程序。

4.3 最小化死锁

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

而且,开发人员必须确保在设计数据库索引表时要牢记防止死锁和性能。

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

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

4.4 参数传递和验证

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

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

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

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

验证应包括检查以确保:

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

  • 分区之间没有间隙。

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

同样,该体系结构应考虑分区的合并。关键问题包括:

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

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