繁体   English   中英

Spring 集成 - 优先聚合器

[英]Spring Integration - Priority aggregator

我有以下应用要求:

  • 从 RabbitMq 接收消息,然后根据一些更复杂的规则进行聚合,例如 - 基于types属性(具有预先给定的类型时间映射)和基于现有时间消息已在队列中等待( old属性)
  • 所有消息都应该以某种可变的消息速率发布,例如 1msg/sec 到 100msg/sec。 此速率由将监控 rabbitmq 队列大小的服务控制和设置(一个与此组件无关的队列,并且在管道中更远),如果队列中的消息过多 - 将降低速率。

正如您在图像中看到的一个用例:三条消息已经聚合并等待下一秒发布(因为当前速率为1msg/sec ),但就在那时, MSGid:10到达,它更新了AGGREGATED 2 ,使其成为优先级的第一条消息。 所以在下一个滴答声中,我们没有释放AGGREGATED 3 ,而是释放AGGREGATED 2 ,因为它现在具有更高的优先级。

在此处输入图像描述

现在,问题是 - 我可以为此使用 Spring 集成聚合器,因为我不知道它是否支持聚合期间的消息优先级? 我知道groupTimeout ,但那只是调整单个消息组 - 不改变其他组的优先级。 是否可以使用MessageGroupStoreReaper在新的 MSG 到达时按优先级调整所有其他聚合消息?

更新

我做了一些这样的实现——现在看起来还可以——它在消息到达时聚合消息,比较器通过我的自定义逻辑对消息进行排序。

您认为这可能存在一些问题(并发等)吗? 我可以在日志中看到,轮询器在某些情况下被多次调用。 这是正常的吗?

2021-01-18 13:52:05.277  INFO 16080 --- [   scheduling-1] ggregatorConfig$PriorityAggregatingQueue : POLL
2021-01-18 13:52:05.277  INFO 16080 --- [   scheduling-1] ggregatorConfig$PriorityAggregatingQueue : POLL
2021-01-18 13:52:05.277  INFO 16080 --- [   scheduling-1] ggregatorConfig$PriorityAggregatingQueue : POLL
2021-01-18 13:52:05.277  INFO 16080 --- [   scheduling-1] ggregatorConfig$PriorityAggregatingQueue : POLL

另外,这个注释的doit方法是在运行时增加最大轮询消息数的正确方法吗?

@Bean
    public MessageChannel aggregatingChannel(){
        return new QueueChannel(new PriorityAggregatingQueue<>((m1, m2) -> {//aggr here},
                Comparator.comparingInt(x -> x),
                (m) -> {
                    ExampleDTO d = (ExampleDTO) m.getPayload();
                    return d.getId();
                }
        ));
    }

    class PriorityAggregatingQueue<K> extends AbstractQueue<Message<?>> {
        private final Log logger = LogFactory.getLog(getClass());
        private final BiFunction<Message<?>, Message<?>, Message<?>> accumulator;
        private final Function<Message<?>, K> keyExtractor;
        private final NavigableMap<K, Message<?>> keyToAggregatedMessage;

        public PriorityAggregatingQueue(BiFunction<Message<?>, Message<?>, Message<?>> accumulator,
                                        Comparator<? super K> comparator,
                                        Function<Message<?>, K> keyExtractor) {
            this.accumulator = accumulator;
            this.keyExtractor = keyExtractor;
            keyToAggregatedMessage = new ConcurrentSkipListMap<>(comparator);
        }

        @Override
        public Iterator<Message<?>> iterator() {
            return keyToAggregatedMessage.values().iterator();
        }

        @Override
        public int size() {
            return keyToAggregatedMessage.size();
        }

        @Override
        public boolean offer(Message<?> m) {
            logger.info("OFFER");
            return keyToAggregatedMessage.compute(keyExtractor.apply(m), (k,old) -> accumulator.apply(old, m)) != null;
        }

        @Override
        public Message<?> poll() {
            logger.info("POLL");
            Map.Entry<K, Message<?>> m = keyToAggregatedMessage.pollLastEntry();
            return m != null ? m.getValue() : null;
        }

        @Override
        public Message<?> peek() {
            Map.Entry<K, Message<?>> m = keyToAggregatedMessage.lastEntry();
            return m!= null ? m.getValue() : null;
        }
    }

//    @Scheduled(fixedDelay = 10*1000)
//    public void doit(){
//        System.out.println("INCREASE POLL");
//        pollerMetadata().setMaxMessagesPerPoll(pollerMetadata().getMaxMessagesPerPoll() * 2);
//    }

    @Bean(name = PollerMetadata.DEFAULT_POLLER)
    public PollerMetadata pollerMetadata(){
        PollerMetadata metadata = new PollerMetadata();
        metadata.setTrigger(new DynamicPeriodicTrigger(Duration.ofSeconds(30)));
        metadata.setMaxMessagesPerPoll(1);
        return metadata;
    }

    @Bean
    public IntegrationFlow aggregatingFlow(
            AmqpInboundChannelAdapter aggregatorInboundChannel,
            AmqpOutboundEndpoint aggregatorOutboundChannel,
            MessageChannel wtChannel,
            MessageChannel aggregatingChannel,
            PollerMetadata pollerMetadata
    ) {
    return IntegrationFlows.from(aggregatorInboundChannel)
        .wireTap(wtChannel)
        .channel(aggregatingChannel)
        .handle(aggregatorOutboundChannel)
        .get();
    }

好吧,如果有一条新消息需要组完成,它会到达聚合器,那么这样的组会立即释放(如果您的ReleaseStrategy这么说的话)。 超时组的 rest 将继续等待调度。

可能会想出智能算法来依靠MessageGroupStoreReaper的单个公共调度来决定我们是否需要释放该部分组或只是丢弃它。 再说一遍: ReleaseStrategy应该给我们一个发布与否的线索,即使是部分发布。 当丢弃发生并且我们希望将这些消息保留在聚合器中时,我们需要在延迟一段时间后将它们重新发送回聚合器。 过期后,组从存储中删除,这发生在我们已经发送到丢弃通道时,所以最好延迟它们并让聚合器清理这些组,这样延迟后我们可以安全地将它们发送回作为新组的一部分的新到期期限的聚合器。

您可能还可以在发布普通组后迭代存储中的所有消息,以调整其标题中的一些时间键以用于下一个到期时间。

我知道这很困难,但实际上没有任何开箱即用的解决方案,因为它的设计目的不是为了影响我们刚刚处理的其他群体......

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM