簡體   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