如何使用Spring Integration Java DSL消耗通道中的所有消息?

时间:2019-08-06 10:13:23

标签: spring-integration spring-integration-dsl

我正在尝试为单线程处理程序定义流。消息数量众多,处理程序速度很慢(一一处理消息效率低下)。因此,我想让处理程序使用Java DSL一次使用通道中所有可用消息(或等待直到积累了一些消息)。如果通道中没有消息,并且处理程序已经处理了先前的组,则它应等待一段时间(超时“ a”),以便一些消息在通道中累积。但是,如果消息继续出现,则处理程序务必在上一次执行后的一段时间(超时“ b”)后使用它们。因此,处理程序执行之间的时间间隔不应超过“ b”(除非没有消息到达通道)。

没有理由制作这种处理程序的多个实例:它为接口生成数据。下面的代码描述了一些基本配置。我的问题是,一旦处理程序执行完成,我就无法提出去抖动(超时“ b”)和释放组的问题。

    @Configuration
    public class SomeConfig {

        private AtomicBoolean someHandlerBusy = new AtomicBoolean(false);

        @Bean
        StandardIntegrationFlow someFlow() {
            return IntegrationFlows
                    .from("someChannel")
                    .aggregate(aggregatorSpec -> aggregatorSpec
                                    //The only rule to release a group:
                                    //wait 500ms after last message and have a free someHandler
                                    .groupTimeout(500)
                                    .sendPartialResultOnExpiry(true) //if 500ms expired - send group
                                    .expireGroupsUponCompletion(true) //group should be filled again
                                    .correlationStrategy(message -> true) //one group key, all messages in oe group
                                    .releaseStrategy(message -> false) //never release messages, only with timeout

                                    //Send messages one by one. This is not part of this task.
                                    //I just want to know how to do that. Like splitter.
                                    //.outputProcessor(MessageGroup::getMessages)
                    )
                    .handle("someHandler")
                    .get();
        }
    }

我有使用普通Java(kotlin)代码的解决方案:https://pastebin.com/mti3Y5tD


更新

以下配置不会删除组。该小组在不断壮大,最终陷入机智。

错误:

*** java.lang.instrument ASSERTION FAILED ***: "!errorOutstanding" with message transform method call failed at JPLISAgent.c line: 844

配置:

    @Configuration
    public class InterfaceHandlerConfigJava {


        @Bean
        MessageChannel interfaceAggregatorFlowChannel() {
            return MessageChannels.publishSubscribe("interfaceAggregatorFlowChannel").get();
        }

        @EventListener(ApplicationReadyEvent.class)
        public void initTriggerPacket(ApplicationReadyEvent event) {
            MessageChannel channel = event.getApplicationContext().getBean("interfaceAggregatorFlowChannel", MessageChannel.class);
            channel.send(MessageBuilder.withPayload(new InterfaceHandler.HandlerReadyMessage()).build());
        }

        @Bean
        StandardIntegrationFlow someFlow(
                InterfaceHandler interfaceHandler
        ) {
            long lastMessageTimeout = 10L;
            return IntegrationFlows
                    .from("interfaceAggregatorFlowChannel")
                    .aggregate(aggregatorSpec -> aggregatorSpec
                            .groupTimeout(messageGroup -> {
                                if (haveInstance(messageGroup, InterfaceHandler.HandlerReadyMessage.class)) {
                                    System.out.println("case HandlerReadyMessage");
                                    if (haveInstance(messageGroup, DbChangeStreamConfiguration.InitFromDbMessage.class)) {
                                        System.out.println("case InitFromDbMessage");
                                        return 0L;
                                    } else if (messageGroup.size() > 1) {
                                        long groupCreationTimeout =
                                                messageGroup.getTimestamp() + 500L - System.currentTimeMillis();
                                        long timeout = Math.min(groupCreationTimeout, lastMessageTimeout);

                                        System.out.println("case messageGroup.size() > 1, timeout: " + timeout);
                                        return timeout;
                                    }
                                }
                                System.out.println("case Handler NOT ReadyMessage");
                                return null;
                            })
                            .sendPartialResultOnExpiry(true)
                            .expireGroupsUponCompletion(true)
                            .expireGroupsUponTimeout(true)
                            .correlationStrategy(message -> true)
                            .releaseStrategy(message -> false)
                    )
                    .handle(interfaceHandler, "handle")
                    .channel("interfaceAggregatorFlowChannel")
                    .get();
        }

        private boolean haveInstance(MessageGroup messageGroup, Class clazz) {
            for (Message<?> message : messageGroup.getMessages()) {
                if (clazz.isInstance(message.getPayload())) {
                    return true;
                }
            }
            return false;
        }
    }

我想强调:此流程处于循环中。有输入,没有输出。消息进入IN,但是处理程序在最后发出HandlerReadyMessage。 也许应该有一些线程中断程序通道?


最终变量

由于聚合器和处理程序不应互相阻塞,也不应尝试产生stackoverflow异常,因此它们应在不同的线程中运行。在上面的配置中,这是通过队列通道实现的。看起来发布-订阅频道没有在不同线程中运行订阅者(至少对于一个订阅者而言)。

    @Configuration
    public class InterfaceHandlerConfigJava {

        // acts as thread breaker too
        @Bean
        MessageChannel interfaceAggregatorFlowChannel() {
            return MessageChannels.queue("interfaceAggregatorFlowChannel").get();
        }

        @Bean
        MessageChannel threadBreaker() {
            return MessageChannels.queue("threadBreaker").get();
        }

        @EventListener(ApplicationReadyEvent.class)
        public void initTriggerPacket(ApplicationReadyEvent event) {
            MessageChannel channel = event.getApplicationContext().getBean("interfaceAggregatorFlowChannel", MessageChannel.class);
            channel.send(MessageBuilder.withPayload(new InterfaceHandler.HandlerReadyMessage()).build());
        }

        @Bean
        StandardIntegrationFlow someFlow(
                InterfaceHandler interfaceHandler
        ) {
            long lastMessageTimeout = 10L;
            return IntegrationFlows
                    .from("interfaceAggregatorFlowChannel")
                    .aggregate(aggregatorSpec -> aggregatorSpec
                            .groupTimeout(messageGroup -> {
                                if (haveInstance(messageGroup, InterfaceHandler.HandlerReadyMessage.class)) {
                                    System.out.println("case HandlerReadyMessage");
                                    if (haveInstance(messageGroup, DbChangeStreamConfiguration.InitFromDbMessage.class)) {
                                        System.out.println("case InitFromDbMessage");
                                        return 0L;
                                    } else if (messageGroup.size() > 1) {
                                        long groupCreationTimeout =
                                                messageGroup.getTimestamp() + 500L - System.currentTimeMillis();
                                        long timeout = Math.min(groupCreationTimeout, lastMessageTimeout);

                                        System.out.println("case messageGroup.size() > 1, timeout: " + timeout);
                                        return timeout;
                                    }
                                }
                                System.out.println("case Handler NOT ReadyMessage");
                                return null;
                            })
                            .sendPartialResultOnExpiry(true)
                            .expireGroupsUponCompletion(true)
                            .expireGroupsUponTimeout(true)
                            .correlationStrategy(message -> true)
                            .releaseStrategy(message -> false)
                            .poller(pollerFactory -> pollerFactory.fixedRate(1))
                    )
                    .channel("threadBreaker")
                    .handle(interfaceHandler, "handle", spec -> spec.poller(meta -> meta.fixedRate(1)))
                    .channel("interfaceAggregatorFlowChannel")
                    .get();
        }

        private boolean haveInstance(MessageGroup messageGroup, Class clazz) {
            for (Message<?> message : messageGroup.getMessages()) {
                if (clazz.isInstance(message.getPayload())) {
                    return true;
                }
            }
            return false;
        }
    }

1 个答案:

答案 0 :(得分:0)

不清楚计时器b的含义,但是您可以使用.groupTimeoutExpression(...)动态确定组超时。

您不必担心一一发送消息;当输出处理器返回Message<?>的集合时,它们一次发送一次。