【问题标题】:Avoid processing same file twice in Spring Batch避免在 Spring Batch 中两次处理相同的文件
【发布时间】:2017-11-14 15:28:40
【问题描述】:

我必须编写一个 Spring Batch 作业如下:

  • 第 1 步:从文件系统加载 XML 文件并将其内容写入数据库临时表
  • 第 2 步:调用 Oracle PL/SQL 过程来处理暂存表。

(欢迎对该工作结构发表评论,但不是问题)。

在第 1 步中,我想在加载 XML 文件后将其移动到另一个目录。我希望这尽可能与对临时表的写入“事务性”。也就是说,对暂存的写入和文件移动要么都成功,要么都不成功。

我觉得这是必要的,因为如果 (A) 暂存写入发生但文件没有移动,下一次运行将再次拾取文件并再次处理它,以及 (B) 如果文件被移动但暂存写入执行不会发生,那么我们将错过该文件的处理。

这个接口的要求都是关于健壮性。我知道我可以在最后放置一个步骤执行侦听器来移动所有文件,但我希望这种方法能够保证我们永远不会错过处理数据并且永远不会两次处理同一个文件。

部分困难在于我使用的是MultiResourceItemReader。我读到ChunkListener.beforeChunk() 作为块事务的一部分发生,所以我尝试制作一个自定义块CompletionPolicy 以在每次更改资源(文件)名称后强制块完成,但我无法让它工作。在任何情况下,我都需要一个afterChunk() 监听器,这无论如何都不是事务的一部分。

我将就我的具体问题提供任何指导,或者就如何在 Spring Batch 中稳健地处理文件(我只是在学习)进行专家解释。谢谢!

【问题讨论】:

    标签: spring spring-batch batch-processing


    【解决方案1】:

    我现在有非常相似的春季批处理。

    春季批次非常适合您的要求。

    我建议从这里开始使用 spring 集成。 在 spring 集成中,您可以配置为监视您的文件夹,然后使其触发批处理作业。 official documentation 中有很好的例子。

    那么你应该使用spring批处理的强大概念——识别参数。 Spring 批处理作业使用唯一的参数运行,如果您将此参数作为标识,则无法使用相同的参数生成其他作业(尽管您可以重新启动原始作业)。

    /**
     * Add a new String parameter for the given key.
     *
     * @param key - parameter accessor.
     * @param parameter - runtime parameter
     * @param identifying - indicates if the parameter is used as part of identifying a job instance
     * @return a reference to this object.
     */
    public JobParametersBuilder addString(String key, String parameter, boolean identifying) {
        parameterMap.put(key, new JobParameter(parameter, identifying));
        return this;
    }
    

    所以在这里您需要问自己,批处理作业的唯一标识约束是什么?我建议它是完整的文件路径。但是你需要确保没有人提供具有相同文件名的不同文件。 spring 集成还可以查看文件是否已被应用程序看到并忽略它。请查看documentation on AcceptOnceFileListFilter

    如果您希望批量保证“类似事务”的逻辑 - 请不要将其放入侦听器中,请创建一个将移动文件的特定步骤。听众很适合补充逻辑。 这样,如果此步骤由于任何原因失败,您仍然可以解决问题并重试工作。

    【讨论】:

    • 谢谢!我一直在考虑引入 Spring Integration,因为下一个接口需要传输文件。我会尝试这个答案,如果可行,我会接受。 (同时投了赞成票——谢谢!)
    • 实际上,我忘记了——他们不想轮询文件。他们只想在特定时间检查。但是,我可以不使用 Spring Integration,而是创建一个通用的启动批处理作业来检查文件,然后为每个文件名生成一个单独的作业,对吧?
    • @MatthewMcPeak AFAIK spring 集成还具有基于 cron 表达式的轮询器 - 因此很容易想出您的自定义计划触发器,您只需根据需要将它们组合起来
    • 接受您的回答。我最终创建了两个 Spring Batch 作业:一个文件监视器作业,它(通过MultiResourcePartitioner)将为每个文件运行一个单独的JobStep。然后 Job 运行为 JobStep 将在步骤 1 中暂存文件并在步骤 2 中移动文件。再次感谢!
    【解决方案2】:

    这种过程可以通过 2 步和 1 个侦听器的作业轻松完成:

    1. 标准(从 XML 读取 -> 进程?-> 写入 DB)步骤;你不关心可重启性,因为 SB 足够聪明,可以避免数据读取重复
    2. 附加到步骤 1 的侦听器,用于在成功执行步骤后移动文件(example 1example 2example 3
    3. 数据处理的第二步

    #3 可以作为步骤 1 过程阶段插入

    【讨论】:

    • 如果#2(文件移动)失败会怎样?如何在不重新处理数据的情况下重新启动作业?我不知道您所说的“Spring Batch 足够聪明,可以避免数据重复”是什么意思。我知道它足够聪明,可以避免两次运行同一组作业参数,但是如果由于步骤侦听器中的故障而必须重新启动,我看不出这如何拯救我。能详细点吗?
    • 关于块处理和可重启性我的建议是阅读 SB 官方文档或网络上的一些教程。如果由于某种原因文件移动失败,作业应标记为 FAILED 并需要手动解决
    • 谢谢。我试图避免这种情况。我想创建一个容错且在失败时可重新启动的批处理。
    猜你喜欢
    • 1970-01-01
    • 2014-06-03
    • 2016-06-29
    • 2020-07-30
    • 2013-04-28
    • 2012-05-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多