2021-07-16 spring batch

概览

Spring Batch是一个轻量级、全面的批处理框架,旨在支持开发健壮的批处理应用程序,这对企业系统的日常操作至关重要。
Spring Batch提供了在处理大量记录时必不可少的可重用功能,包括日志/跟踪、事务管理、作业处理统计、作业重启、跳过和资源管理。它还提供了更先进的技术服务和特性,通过优化和分区技术,这些服务和特性将支持极高容量和高性能的批处理作业。简单和复杂的高容量批处理作业都可以以高度可伸缩的方式利用该框架来处理大量信息。

简介

企业领域中的许多应用程序需要批量处理来在关键任务环境中执行业务操作。这些业务运作包括:
大量信息的自动化、复杂处理,在没有用户交互的情况下处理效率最高。这些操作通常包括基于时间的事件(如月末计算、通知或通信)。
跨非常大的数据集(例如,保险收益确定或费率调整)重复处理的复杂业务规则的定期应用。
将从内部和外部系统接收到的信息集成到记录系统中,这些信息通常需要以事务性的方式进行格式化、验证和处理。批处理每天为企业处理数十亿笔交易。
Spring Batch是一个轻量级、全面的批处理框架,旨在开发健壮的批处理应用程序,这对企业系统的日常操作至关重要。Spring Batch构建在人们所期望的Spring框架特性(生产力、基于pojo的开发方法和通用易用性)之上,同时使开发人员在必要时更容易访问和利用更高级的企业服务。Spring Batch不是一个调度框架。商业和开放源码领域中都有许多优秀的企业调度器(如Quartz、Tivoli、Control-M等)。它旨在与调度器一起工作,而不是取代调度器。
Spring Batch提供了在处理大量记录时必不可少的可重用功能,包括日志/跟踪、事务管理、作业处理统计、作业重启、跳过和资源管理。它还提供了更先进的技术服务和特性,通过优化和分区技术支持极高容量和高性能的批处理作业。Spring Batch既可以用于简单的用例(如将文件读入数据库或运行存储过程),也可以用于复杂的大容量用例(如在数据库之间移动大容量数据,转换数据,等等)。高容量批处理作业可以以高度可伸缩的方式利用该框架来处理大量信息。

背景

虽然开源软件项目和相关社区把更多的注意力集中在基于web和微服务的体系结构框架上,但明显缺乏对可重用体系结构框架的关注,以适应基于java的批处理需求,尽管仍然需要在企业IT环境中处理此类处理。缺乏标准的、可重用的批处理体系结构导致了在客户企业IT功能中开发的许多一次性的内部解决方案的激增。
SpringSource(现为Pivotal)和埃森哲(Accenture)合作改变了这一现状。埃森哲在实现批处理架构方面的实践行业和技术经验、SpringSource的技术深度和Spring经过验证的编程模型共同构成了一个自然而强大的伙伴关系,可以创建高质量的、与市场相关的软件,旨在填补企业Java的一个重要空白。这两家公司都与许多客户合作,他们通过开发基于spring的批处理体系结构解决方案来解决类似的问题。这提供了一些有用的附加细节和现实约束,有助于确保解决方案可以应用于客户提出的现实问题。
Accenture为Spring batch项目贡献了以前专有的批处理体系结构框架,以及提交者资源,以推动支持、增强和现有的特性集。Accenture的贡献是基于在过去几代平台(COBOL/Mainframe, c++ /Unix,现在是Java/anywhere)中构建批处理架构的几十年经验。
埃森哲和SpringSource的合作旨在促进软件处理方法、框架和工具的标准化,企业用户在创建批处理应用程序时可以一致地利用这些方法、框架和工具。希望向企业IT环境提供标准的、经过验证的解决方案的公司和政府机构可以从Spring Batch中受益。

使用场景

一个典型的批处理程序一般是:
从数据库、文件或队列中读取大量记录。
以某种方式处理数据。
以修改后的形式回写数据。
Spring Batch自动化了这个基本的批处理迭代,提供了将类似事务作为一个集合处理的能力,通常是在没有任何用户交互的脱机环境中。批处理作业是大多数IT项目的一部分,Spring Batch是唯一提供健壮的企业级解决方案的开源框架。
业务场景
定期提交批处理过程
并发批处理:对作业进行并行处理
分阶段的、企业消息驱动的处理
大规模并行批处理
故障后手动或定时重启
依赖步骤的顺序处理(扩展到工作流驱动的批)
部分处理:跳过记录(例如,回滚时)
整批事务,用于小批大小或现有存储过程/脚本的情况
技术目标
批处理开发人员使用Spring编程模型:专注于业务逻辑,让框架负责基础设施。
基础设施、批处理执行环境和批处理应用程序之间的关注点清晰分离。
提供公共的核心执行服务作为所有项目都可以实现的接口。
提供核心执行接口的简单和默认实现,可以“开箱即用”。
通过在所有层中利用spring框架,易于配置、定制和扩展服务。
所有现有的核心服务都应该易于替换或扩展,不会对基础设施层产生任何影响。
提供一个简单的部署模型,体系结构jar完全独立于应用程序,使用Maven构建。

Spring Batch架构

Spring Batch的设计考虑到了可扩展性和不同的最终用户群体。下图显示了支持最终用户开发人员可扩展性和易用性的分层体系结构。
Spring Batch分层体系结构
这个分层的体系结构突出了三个主要的高级组件:应用程序、核心和基础设施。该应用程序包含所有的批处理作业和开发人员使用Spring batch编写的自定义代码。批处理核心包含启动和控制批处理作业所需的核心运行时类。它包括JobLauncher、Job和Step的实现。Application和Core都构建在公共基础设施之上。此基础设施包含常见的读取器、写入器和服务(如RetryTemplate),应用程序开发人员(读取器和写入器,如ItemReader和ItemWriter)和核心框架本身(重试,这是它自己的库)都使用这些服务。

通用批处理原则和指南

在构建批处理解决方案时,应该考虑以下关键原则、指导原则和一般考虑事项。
请记住,批处理体系结构通常会影响在线体系结构,反之亦然。在设计时考虑到架构和环境,尽可能使用公共构建块。
尽可能简化并避免在单个批处理应用程序中构建复杂的逻辑结构。
将数据的处理和存储紧密地放在一起(换句话说,将数据保存在处理发生的地方)。
尽量减少系统资源的使用,特别是I/O。在内存中执行尽可能多的操作。
检查应用程序I/O(分析SQL语句)以确保避免不必要的物理I/O。特别需要注意的是以下四个常见的缺陷:
当数据可以读取一次并缓存或保存在工作存储中时,为每个事务读取数据。
为先前在同一事务中读取数据的事务重新读取数据。
导致不必要的表或索引扫描。
在SQL语句的WHERE子句中不指定键值。
不要在批处理运行中重复执行某项操作。例如,如果您需要为报告目的进行数据汇总,那么您应该(如果可能)在初始处理数据时增加存储的总数,这样您的报告应用程序就不必重新处理相同的数据。
在批处理应用程序开始时分配足够的内存,以避免在过程中进行耗时的重新分配。
总是对数据完整性做最坏的假设。插入足够的检查和记录验证以保持数据的完整性。
尽可能实现内部验证的校验和。例如,平面文件应该有一个预告记录,告知文件中记录的总数和关键字段的聚合。
在具有实际数据量的类似生产环境中,尽可能早地计划和执行压力测试。
在大型批处理系统中,备份可能具有挑战性,特别是当系统24-7小时在线并发运行时。数据库备份通常在联机设计中得到很好的处理,但是文件备份也应该被认为同样重要。如果系统依赖于平面文件,那么文件备份过程不仅应该到位并形成文档,还应该定期进行测试。

批处理策略

为了帮助设计和实现批处理系统,应该以样本结构图和代码壳的形式向设计人员和程序员提供基本的批处理应用程序构建块和模式。在开始设计批处理作业时,应该将业务逻辑分解为一系列步骤,这些步骤可以使用以下标准构建块实现:
转换应用程序:对于由外部系统提供或生成给外部系统的每种类型的文件,必须创建转换应用程序,将提供的事务记录转换为处理所需的标准格式。这种批处理应用程序可以部分或全部由翻译实用程序模块组成(请参阅基本批处理服务)。
验证应用程序:验证应用程序确保所有输入/输出记录是正确和一致的。验证通常基于文件头和尾部、校验和和验证算法,以及记录级别的交叉检查。
Extract Applications:从数据库或输入文件中读取一组记录,根据预定义的规则选择记录,并将记录写入输出文件的应用程序。
提取/更新应用程序:从数据库或输入文件中读取记录,并由每个输入记录中的数据驱动对数据库或输出文件进行更改的应用程序。
处理和更新应用程序:对来自提取或验证应用程序的输入事务执行处理的应用程序。处理通常包括读取数据库以获得处理所需的数据,可能会更新数据库并为输出处理创建记录。
输出/格式应用程序:读取输入文件,根据标准格式从记录中重组数据,并生成输出文件以打印或传输到另一个程序或系统的应用程序。
此外,应该为不能使用前面提到的构建块构建的业务逻辑提供一个基本的应用程序外壳。
除了主要的构建块,每个应用程序可以使用一个或多个标准的实用程序步骤,例如:
排序:一种程序,它读取输入文件并生成一个输出文件,在输出文件中,记录根据记录中的排序键字段重新排序。排序通常由标准的系统实用程序执行。
分割:读取单个输入文件并根据字段值将每条记录写入多个输出文件中的一个的程序。拆分可以通过参数驱动的标准系统实用程序进行裁剪或执行。
合并:从多个输入文件中读取记录,并将输入文件中的数据合并生成一个输出文件的程序。可以通过参数驱动的标准系统实用程序定制或执行合并。
批处理应用程序还可以根据它们的输入源进行分类:
数据库驱动的应用程序由从数据库中检索的行或值驱动。
文件驱动的应用程序由从文件中检索的记录或值驱动。
消息驱动的应用程序由从消息队列中检索的消息驱动。
任何批处理系统的基础都是处理策略。影响策略选择的因素包括:估计的批系统容量、与在线系统或其他批系统的并发性、可用的批窗口。(注意,随着越来越多的企业想要24 * 7地运行,清晰的批处理窗口正在消失)。
典型的批处理选项有(按实现复杂性的递增顺序):
在脱机模式下的批处理窗口期间的正常处理。
并发批处理或联机处理。
许多不同批处理同时运行或作业的并行处理。
分区(同时处理同一作业的多个实例)。
以上选项的组合。
商业调度器可能支持这些选项中的一部分或全部。
下面的部分将更详细地讨论这些处理选项。需要注意的是,根据经验,批处理进程采用的提交和锁定策略取决于执行的处理类型,并且在线锁定策略也应该使用相同的原则。因此,在设计整体体系结构时,批处理体系结构不能只是一个事后考虑的问题。
锁定策略可以是只使用普通数据库锁,或者在体系结构中实现额外的自定义锁定服务。锁定服务将跟踪数据库锁定(例如,将必要的信息存储在专用的db表中),并向请求数据库操作的应用程序授予或拒绝权限。该体系结构还可以实现重试逻辑,以避免在出现锁情况时中止批处理作业。

1.

对于在单独的批处理窗口中运行的简单批处理进程,在线用户或其他批处理进程不需要更新数据,并发性不是问题,可以在批处理运行结束时完成单个提交。
在大多数情况下,更健壮的方法更合适。请记住,随着时间的推移,批处理系统的复杂性和处理的数据量都有增长的趋势。如果没有锁定策略,并且系统仍然依赖于单个提交点,则修改批处理程序可能是痛苦的。因此,即使是最简单的批处理系统,也需要考虑重新启动-恢复选项的提交逻辑,以及本节稍后描述的更复杂情况的相关信息。

2.

并发批处理或联机处理处理可由联机用户同时更新的数据的批处理应用程序不应锁定联机用户可能需要数秒以上的任何数据(数据库或文件中)。此外,更新应该在每几个事务结束时提交到数据库。这将减少其他进程不可用的数据部分和不可用数据的运行时间。
最小化物理锁定的另一个选择是使用乐观锁定模式或悲观锁定模式实现逻辑行级锁定。
乐观锁定假定记录争用的可能性很低。它通常意味着在批处理和在线处理并发使用的每个数据库表中插入一个时间戳列。当应用程序获取一行进行处理时,它还将获取时间戳。当应用程序随后尝试更新已处理的行时,更新将使用WHERE子句中的原始时间戳。如果时间戳匹配,则更新数据和时间戳。如果时间戳不匹配,则表明另一个应用程序在获取和更新尝试之间更新了同一行。因此,无法进行升级。
悲观锁定是假设有很高的记录争用可能性,因此需要在检索时获得物理或逻辑锁的任何锁定策略。悲观逻辑锁定的一种类型是在数据库表中使用专用的锁列。当应用程序检索要更新的行时,它会在锁列中设置一个标志。设置了该标志后,其他试图检索同一行的应用程序将在逻辑上失败。当设置该标志的应用程序更新该行时,它也会清除该标志,从而使其他应用程序能够检索该行。请注意,在初始获取和设置标志之间必须保持数据的完整性,例如使用db锁(例如SELECT for UPDATE)。还需要注意的是,这种方法与物理锁定有着相同的缺点,只是构建一个超时机制(如果用户去吃午饭,而记录被锁定,那么就会释放锁)相对容易一些。
这些模式不一定适合于批处理,但是它们可以用于并发批处理和在线处理(例如,在数据库不支持行级锁定的情况下)。一般来说,乐观锁定更适合在线应用程序,而悲观锁定更适合批处理应用程序。每当使用逻辑锁时,必须对访问受逻辑锁保护的数据实体的所有应用程序使用相同的方案。
注意,这两种解决方案都只针对锁定单个记录。通常,我们可能需要锁定一组逻辑相关的记录。对于物理锁,您必须非常小心地管理它们,以避免潜在的死锁。对于逻辑锁,通常最好构建一个逻辑锁管理器,该管理器能够理解您想要保护的逻辑记录组,并且能够确保锁是一致的和非死锁。这个逻辑锁管理器通常使用它自己的表进行锁管理、争用报告、超时机制和其他关注事项。

3.

并行处理并行处理允许多个批处理运行或作业并行运行,以减少累计批处理时间。只要作业不共享相同的文件、db-table或索引空间,这就不是问题。如果是这样,则应该使用分区数据实现此服务。另一种选择是通过使用控制表构建用于维护相互依赖关系的体系结构模块。控制表应该包含每个共享资源的一行,以及应用程序是否正在使用该资源。然后,并行作业中的批处理体系结构或应用程序将从该表中检索信息,以确定它是否能够访问所需的资源。
如果数据访问不是问题,可以通过使用并行处理的其他线程来实现并行处理。在大型机环境中,传统上使用并行作业类,以确保所有进程有足够的CPU时间。无论如何,该解决方案必须足够健壮,以确保所有运行过程的时间片。
并行处理中的其他关键问题包括负载平衡和一般系统资源(如文件、数据库缓冲池等)的可用性。还要注意,控制表本身很容易成为关键资源。

4.

分区使用分区允许多个版本的大型批处理应用程序并发运行。这样做的目的是减少处理长批作业所需的时间。可以成功分区的进程是那些可以对输入文件进行分割和/或对主数据库表进行分区以允许应用程序针对不同的数据集运行的进程。
此外,必须将被分区的进程设计为只处理其分配的数据集。分区体系结构必须与数据库设计和数据库分区策略紧密相连。请注意,数据库分区并不一定意味着数据库的物理分区,尽管在大多数情况下这是可取的。下图演示了分区方法:
分区的过程
体系结构应该足够灵活,允许动态配置分区的数量。应该考虑自动配置和用户控制配置。自动配置可以基于输入文件大小和输入记录的数量等参数。

4.1 分区方法必须根据具体情况来选择分区方法。下面的列表描述了一些可能的分区方法:
1. 修正甚至打破记录的问题

这涉及到将输入记录集分割成偶数个部分(例如,10,其中每个部分恰好拥有整个记录集的十分之一)。然后,批处理/提取应用程序的一个实例处理每个部分。
为了使用这种方法,需要进行预处理来分割记录设置。这种分割的结果将是一个下界和上界放置数,可以作为批/提取应用程序的输入,以便将其处理限制为仅其部分。
预处理可能是一个很大的开销,因为它必须计算和确定记录集的每个部分的边界。

2. 以一个键柱分开

这涉及到按键列(比如位置代码)分解输入记录集,并将每个键的数据分配给批处理实例。为了实现这一点,列值可以是:
由分区表分配给批处理实例(将在本节稍后进行描述)。
按值的一部分分配给批处理实例(例如0000-0999,1000 - 1999,等等)。
在选项1中,添加新值意味着手动重新配置批处理/提取,以确保将新值添加到特定的实例中。
在选项2中,这确保通过批处理作业的实例覆盖所有值。但是,一个实例处理的值的数量取决于列值的分布(在0000-0999范围内可能有很多位置,而在1000-1999范围内可能很少)。在此选项下,在设计数据范围时应该考虑分区。
在这两种选择中,都不能实现记录的最优均匀分布到批处理实例。没有使用的批处理实例数量的动态配置。

3.分手的看法

这种方法基本上是按键列分解的,但是是在数据库级别上。它涉及到将记录集分解为视图。批处理应用程序的每个实例在处理过程中使用这些视图。拆分是通过分组数据来完成的。
使用此选项,必须将批处理应用程序的每个实例配置为命中特定视图(而不是主表)。此外,随着新数据值的添加,必须将这组新数据包含到视图中。不存在动态配置功能,因为实例数量的变化会导致视图的变化。

4. 新增处理指标

这涉及到向输入表添加一个新列,它充当指示符。作为预处理步骤,所有指示器都标记为未处理。在批处理应用程序的记录获取阶段,读取记录的条件是该记录被标记为未处理,一旦读取它们(使用锁),它们就被标记为正在处理中。当该记录完成时,指示符被更新为完成或错误。批处理应用程序的许多实例可以在不进行更改的情况下启动,因为附加列确保一条记录只处理一次。句或两句,顺序是“on completion, indicators are marked as being complete.”)
使用这个选项,表上的I/O会动态增加。对于更新批处理应用程序,这种影响会减少,因为无论如何都必须进行写操作。

5. 将表提取到平面文件

这涉及到将表提取到文件中。然后可以将该文件分割成多个段,用作批处理实例的输入。
使用此选项,将表提取到一个文件并将其分割的额外开销可能会抵消多分区的影响。动态配置可以通过修改文件分割脚本来实现。

6. 哈希列的使用

该方案涉及到向用于检索驱动程序记录的数据库表添加一个散列(键/索引)。此散列有一个指示符,用于确定批处理应用程序的哪个实例处理这一行。例如,如果有三个批处理实例要启动,那么’A’指示符标记一行,用于实例1处理,'B’指示符标记一行,用于实例2处理,‘C’指示符标记一行,用于实例3处理。
然后,用于检索记录的过程将使用一个附加的WHERE子句来选择由特定指示器标记的所有行。这个表中的插入将涉及添加标记字段,该字段将默认为一个实例(例如’A’)。
可以使用一个简单的批处理应用程序来更新指示器,例如在不同的实例之间重新分配负载。当添加了足够多的新行时,可以运行此批处理(任何时候,除非在批处理窗口中),以将新行重新分发到其他实例。
批处理应用程序的其他实例只需要运行上述段落中描述的批处理应用程序,以重新分配指示符以处理新的实例数量。

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

支持使用键列方法在分区数据库表上运行的多分区应用程序的体系结构应该包括用于存储分区参数的中央分区存储库。这提供了灵活性并确保了可维护性。存储库通常由一个表组成,称为分区表。
存储在分区表中的信息是静态的,通常应该由DBA维护。这个表应该包含多分区应用程序的每个分区的一行信息。表中应该有Program ID Code, Partition Number(分区的逻辑ID), Low Value of The db key column for this Partition, High Value of The db key column for this Partition。
在程序启动时,程序id和分区号应该从体系结构(特别是从Control Processing微线程)传递给应用程序。如果使用键列方法,则使用这些变量读取分区表,以确定应用程序要处理的数据范围。此外,在整个处理过程中必须使用分区号:
添加到输出文件/数据库更新,以便合并进程正常工作。
向批处理日志报告正常处理,向体系结构错误处理程序报告任何错误。

4.3减少死锁

当应用程序并行运行或分区时,可能会发生数据库资源争用和死锁。作为数据库设计的一部分,数据库设计团队尽可能地消除潜在的争用情况是至关重要的。
此外,开发人员必须确保在设计数据库索引表时考虑到死锁预防和性能。
死锁或热点经常发生在管理表或体系结构表中,例如日志表、控制表和锁定表。这些问题的影响也应加以考虑。一个实际的压力测试对于识别体系结构中可能的瓶颈至关重要。
为了最小化冲突对数据的影响,体系结构应该在附加到数据库或遇到死锁时提供诸如等待和重试间隔等服务。这意味着有一个内置机制来响应某些数据库返回代码,并等待预定的时间并重新尝试数据库操作,而不是立即发出错误。

4.4参数传递与验证

分区体系结构对于应用程序开发人员应该是相对透明的。该架构应该以分区模式执行与运行应用程序相关的所有任务,包括:
在应用程序启动之前检索分区参数。
在应用程序启动之前验证分区参数。
在启动时将参数传递给应用程序。
验证应包括以下检查:
应用程序有足够的分区来覆盖整个数据范围。
分区之间没有间隔。
如果对数据库进行了分区,可能需要进行一些额外的验证,以确保单个分区不会跨数据库分区。
此外,体系结构应该考虑分区的整合。关键问题包括:
必须在进入下一个作业步骤之前完成所有分区吗?
如果其中一个分区中止,会发生什么?

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值