以编程方式停止在BlockingQueue上同步的Spring批并行流

时间:2016-07-11 13:37:05

标签: spring-batch

我有一个Spring批处理作业,它基本上从文件中读取,处理每一行并写入输出(另一个文件)。 由于处理步骤很昂贵,我想让它在多个线程中运行,但由于读写步骤使用的是文件,因此这些步骤必须在单个线程上运行。 我最终得到3个流,每个流并行运行,每个流一步,在2个BlockingQueues上同步。 读取步骤从文件读取并写入一个队列。 处理步骤是多线程的,从队列中读取,处理并写入另一个队列。 写入步骤,从第二个队列读取并将输出写入另一个文件。

它运作得很好,除了我找不到一个干净,快速的'一切都完成后停止工作的方法。现在我正在使用'民意调查'两个队列都有超时,并假设如果在一段时间内没有项目存在,那么我们就完成了。这会将作业终止延迟指定的秒数,并且我无法使用很短的时间,因为通过某些外力(如机器负载),作业可能会出现延迟。

我尝试使用类似毒丸的东西,但问题是,如果我覆盖' doRead' FlatFileItemReader上的方法,当它获得一个' null' (表示文件结尾)然后这个读者永远不会结束,作业永远不会终止。

有人有建议吗?从文档中我知道我可能只是将"同步"读取步骤(文件)和写入步骤(文件)中的编写器上的读者,但我真的更喜欢不同的解决方案。

2 个答案:

答案 0 :(得分:1)

您只需在阅读器中添加有状态变量即可跟踪作业的结束。

public PoisoningReader<T> extends FlatFileItemReader<T> {
    private boolean endJob = false;

    @Override
    public T doRead() {
        if (endJob) {
            return null;
        }

        T object = super.doRead();
        if (object == null) {
            endJob = true;
            return new PoisonPill();
        }
        return item;
    }

答案 1 :(得分:0)

所以,我会发布我的解决方案,以防任何人感兴趣或面临类似的问题。

我,总结我最终使用了毒药丸,正如Dean Clark建议的那样。 我最终将工作简化为仅使用一个BlockingQueue,但我仍然遇到了如何注入Poison Pill的问题,因为它是一个在步骤之间共享的队列,而不是在一个步骤内...

基本上,不是在读者周围徘徊以返回Poison Pill,而是处理器检测它并忽略它,我只是让Spring Batch正常运行,我只是为负责注射Poison Pill的Step添加了一个监听器。此侦听器将覆盖“afterStep”并将其添加到Queue中。 从队列中读取的步骤将获得Poison Pill和队列的结尾,表示“没有更多的工作要做”并且将通过返回null来正常终止。

另一个'怪癖'就是在一个Job中,从Queue读取的Step配置了一个ThreadPool来并行处理项目,所以我需要杀死/解除阻塞从Queue读取的所有线程。一个很好的技巧是让读者从队列中读取,如果它是毒丸,只需将其重新注入队列并返回null。这样每个线程都可以获得一个Poison Pill并正确终止。