spring聚合消息从聚合器发布两次

时间:2015-01-14 13:53:56

标签: spring spring-integration

我有一个弹簧集成流程,它以通道inboundadapter开头并拾取文件并将它们作为消息传递给系统。 在几个组件之后,消息在“聚合器”聚合,根据发布策略或30秒的组超时从它们发布。 下游处理有另一堆组件,直到最后一个。

我面临的问题是这个, 当我根据相关ID发送33个创建33个“组/桶”的文件时,聚合在“聚合器”上,某些文件或消息似乎被“释放”了两次。我得出结论的原因是因为我有一个频道拦截器,在第一次成功完成下游处理之后,第二次显示通过“已释放”频道(紧接在聚合器之后出现)的一些消息。此外,此行为导致我的应用程序找不到文件并抛出我看到的异常。这使我得出结论,消息桶/组/ corrID以某种方式被“释放”了两次。

我试图调试这么多方法,但实质上,我想知道在释放后如何成功通过单个线程中的所有下游组件的corrID / bucket可以再次“释放”。

我的问题是,我该怎么调试呢?我想知道是什么让这个消息/桶重新出现在聚合器中。 我的聚合器如下,

<int:aggregator id="bufferedFiles" input-channel="inQueueForStage"
        output-channel="released" expire-groups-upon-completion="true"
        send-partial-result-on-expiry="true" release-strategy="releaseHandler"
        release-strategy-method="canRelease"
        group-timeout-expression="size() > 0 ? T(com.att.datalake.ifr.loader.utils.MessageUtils).getAggregatorTimeout(one, @sourceSnapshot) : -1">
        <int:poller fixed-delay="${files.pickup.delay:3000}"
            max-messages-per-poll="${num.files.pickup.per.poll:10}"
            task-executor="executor" />
    </int:aggregator>

聚合器的说明 :size()&gt; 0适用于每个相关存储桶。由于文件名的原因,我发送的33个文件中的每一个都会产生/生成/创建一个新的桶,因此聚合器将有33个桶/组/ corrIds,每个桶只包含一个文件。 因此聚合器SPEL表达式只是说如果没有释放策略,那么如果该组确实至少有一些文件,则在30秒后释放桶/组。

我的频道入站适配器如下:

<int-file:inbound-channel-adapter id="files" channel="dispatchFiles" directory="${source.dir}" scanner="directoryScanner"> <int:poller fixed-delay="${files.pickup.delay:3000}" max-messages-per-poll="${num.files.pickup.per.poll:10}" /> </int-file:inbound-channel-adapter>

日志 这是第一次完成流程的消息日志。调用的完成时间建议到达最后一个组件“completionHandler”SA。 enter image description here message_appearing_twice_causing_error

日志说明: “cor”是正在释放两次的存储桶/ corrId。我得到最后一个例外的原因是因为在第一次,文件被从原始位置删除并处理。所以第二次发生这种错误的释放时,没有什么可以处理的。 从图片中可以看出,第一批/ corrId / bucket在11:09左右处理完成,第二批在11:10左右开始。

一个 重要的一点 我注意到这种行为只发生在我有一个全局通道拦截器时,我正在做一些长时间的处理。当这个拦截器被注释掉时,错误就会消失。

问题: 聚合器是否有可能在任何情况下双重发布批次/ corr?如何让聚合器发出任何日志?

由于

编辑10:15 pm

聚合器后面的通道有一个拦截器,如下所示,

public Message<?> preSend(Message<?> message, MessageChannel channel) { LOGGER.info("******** Releasing from aggregator(interceptor) , corrID:{} at time:{} ********",MessageUtils.getCorrelationId(message), new Date() ); finalReporter.callback(channel.toString(), message); return message; }

从Aggregator到最终的compeltionHandler SA,我有单线程处理 聚合器 - &gt; releasedChannel - &gt;一些SA1 - &gt;一些频道 - &gt; ..... - &gt; completionChannel-&GT; completeSA

当我运行33个分区时,让我们关注corrId =“alh”第一次发布时,它看起来像是, alh first time released 它显示的是线程5释放它并且它应该处理所有下游组件。但它离开了它并开始做其他事情,稍后由不同的线程再次拾起,如下所示, alh second thread-8 这似乎/似乎是问题,

解决方案更新: 我现在做了三件事情来解决这个问题,

  1. 出于某种原因,我的拦截器正在return super.preSend(message, channel)而不是return message。我把它改成了后者

  2. 我有一个全球频道拦截器,我删除了全局并保留了个人拦截器

  3. 如果频道拦截器在返回之前有任何问题,是否会导致新发布?

  4. 虽然我仍然看到图片中描述的上述场景,但我没有得到双重处理尝试,因此它避免了错误。我仍然试图理解这一点。

    我理解它太具体而且难以解释;仍然感谢时间和评论...

2 个答案:

答案 0 :(得分:0)

然而,是的。我认为@GaryRussell是对的:因为你使用expire-groups-upon-completion="true" group-timeout-expression可能会释放一些部分群组,而具有相同correlationId的新消息将形成一个新的群组,该群组由下一个group-timeout。你的size() > 0也不好。这意味着它将在group-timeout之后释放部分组。也许size() > 1?但该群组不能是size() == 0。因为它是在第一条消息上创建的,所以,如果存在gruop,它至少包含一条消息。是的,组可以为空,但在这种情况下,聚合器应标记为expire-groups-upon-completion="false"。在这种情况下,它被标记为completed,并且不允许新消息。

答案 1 :(得分:0)

在努力调试和各种盲目情景之后,我相信至少我有一个解决方法和可能的根本原因。我将尝试概述我修改过的所有内容,

根本原因:

我的拦截器使用常见的回调方法调用Common类。此方法基于请求来自的通道名称,将决定采取的适当操作。这些操作主要是收集数据,递增计数器并持久保存数据库中的一些信息。 看起来他们中的一些人有错误,因此,线程正在死亡,消息重新发布。我不完全确定,如果情况不是这样,请纠正我。 但在我修正了这些错误之后,重新发布的问题似乎已经完全消退或消失了。 难以诊断的原因是因为我无法看到在回调方法调用期间抛出的那些错误;可能是我抓住了他们,也可能是他们迷路了。

我还发现问题只发生在聚合器之后的任何通道拦截器上。聚合器之前的拦截器没有出现任何问题;可能是因为他们更简单......

要调试, 我删除了拦截器并直接从各种组件(SA)进行了回调,删除了全局拦截器,并尝试为特定通道添加单独的拦截器。

感谢您的帮助。