簡體   English   中英

Kafka 消費者 - 暫停從特定 kafka 主題分區輪詢事件以將其用作延遲隊列

[英]Kafka consumer- Pause polling of event from specific kafka topic partition to use it as delayed queue

我們的系統中有一個場景,其中 kafka 主題 XYZ 用戶詳細信息由其他一些生產應用程序 A(不同的系統)發布,而我的應用程序 B 正在使用該主題。

要求是應用程序 B 需要在 A 將其放入 kafka 主題 XYZ 后 45 分鍾(或任何可配置的時間)使用該事件(此延遲的原因是某些系統 C 的另一個 REST api 需要根據此用戶詳細信息觸發)特定用戶確認是否為該用戶設置了一些標志,並且該標志可以在 45 分鍾內的任何時間設置,盡管如果 C 沒有能力發布到 kafka 或通知我們,它可以解決以任何方式)。

我們的應用程序 B 是在 spring 中編寫的。

我嘗試的解決方案是從 Kafka 獲取事件並檢查隊列中第一個事件的時間戳,如果該事件已經 45 分鍾,則處理它,或者如果它少於 45 分鍾,則暫停輪詢 kafka 容器以獲得該數量使用MessageListnerContainer pause()方法達到 45 分鍾的時間。 像下面這樣的 -

@KafkaListener(id = "delayed_listener", topics = "test_topic", groupId = "test_group")
        public void delayedConsumer(@Payload  String message,
                                    Acknowledgment acknowledgment) {

            UserDataEvent userDataEvent = null;
            try {
                 userDataEvent = this.mapper.readValue(message, TopicRequest.class);
            } catch (JsonProcessingException e) {
                logger.error("error while parsing message");
            }
            MessageListenerContainer delayedContainer = this.kafkaListenerEndpointRegistry.getListenerContainer("delayed_listener");
            if (userDataEvent.getPublishTime() > 45 minutes) // this will be some configured value
 {
                long sleepTimeForPolling = userDataEvent.getPublishTime() - System.currentTimeMillis();
                // give negative ack to put already polled messages back to kafka topic
                acknowledgment.nack(1000);
                // pause container, and later resume it  
                delayedContainer.pause();
                ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
                scheduledExecutorService.schedule(() -> {
                    delayedContainer.resume();
                }, sleepTimeForPolling, TimeUnit.MILLISECONDS);
                return;
            }
            // if message was already 45 minutes old then process it
            this.service.processMessage(userDataEvent);
            acknowledgment.acknowledge();
        }

雖然它適用於單個分區,但我不確定這是否是正確的方法,對此有何評論? 我還看到多個分區會導致問題,因為上面的 pause 方法調用將暫停整個容器,如果其中一個分區有舊消息,如果容器因其他分區中的新消息而暫停,則不會使用它。 我可以以某種方式在分區級別使用這個暫停邏輯嗎?

在一定的可配置時間后實現這種延遲處理的任何更好/推薦的解決方案,我可以在這種情況下采用而不是做我上面做的事情?

Kafka 並不是真正為此類場景設計的。

我可以看到該技術工作的一種方法是將容器並發設置為與主題中的分區數相同,以便每個分區由不同線程上的不同使用者處理; 然后暫停/恢復單個Consumer<?, ?> s 而不是整個容器。

為此,添加Consumer<?, ?>作為附加參數; 要恢復消費者,請設置idleEventInterval並檢查事件偵聽ListenerContainerIdleEvent ( ListenerContainerIdleEvent ) 中的計時器。 Consumer<?, ?>是事件的一個屬性,因此您可以在那里調用resume()

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM