如何为Kafka用Spring寻找时间戳的偏移

时间:2018-12-07 11:52:01

标签: java spring-boot apache-kafka timestamp spring-kafka

我们正在使用spring-kafka来消耗必须作为服务器发送事件(SSE)转发给前端的消息。

用户登录后,应该可以看到自上次会话以来错过的所有事件。

如以下所述,当前实现使用ConsumerSeekCallback this answer

但是该回调不支持基础KafkaConsumer(KafkaConsumer#offsetForTimes)的offsetForTimes方法。

所以我必须使用seekToBeginning和filter作为时间戳,当有很多消息时会引起问题...

自给定时间戳记以来,是否还有其他方法仅接收消息? 也许是直接使用消费者的安全方法?

2 个答案:

答案 0 :(得分:1)

2.0引入了ConsumerAwareRebalanceListener(当前版本为2.2.2)。

有关示例,请参见How to test a ConsumerAwareRebalanceListener?

答案 1 :(得分:1)

正如Gary Russel指出的那样,ConsumerSeekCallback是遗留的,所以这是不行的...而且我不会公开GitHub问题...

我终于能够实现自己的目标:

  

当用户登录时,应该看到她的所有事件   自从她上次会议以来错过了比赛。

通过在ListenerContainerIdleEvent的EventListener中处理所有新订阅,消费者可以用作事件数据的一部分:

    @EventListener(condition = "event.listenerId.startsWith('qux-')")
    public void idleEventHandler(ListenerContainerIdleEvent event) {

        // find new subscriptions
        Collection<EventListenerSubscription> newSubscriptions = 
                subscriptions.stream().filter(s -> s.isNew())
                .collect(Collectors.toList());

        if (!newSubscriptions.isEmpty()) {

            // mark subscriptions a not new
            newSubscriptions.forEach(s -> s.setNew(false));

            // compute the oldest time stamp
            OptionalLong oldestTimeStamp = 
                    newSubscriptions.stream()
                    .mapToLong(s -> s.getLastTimeStamp())
                    .reduce(Long::min);

            if (oldestTimeStamp.isPresent()) {

                // seek on topic for oldest time stamp
                Map<TopicPartition, Long> timestampsToSearch = new HashMap<>();
                timestampsToSearch.put(new TopicPartition(eventTopic, 0),
                                       oldestTimeStamp.getAsLong());
                Consumer<?, ?> consumer = event.getConsumer();
                event.getConsumer().offsetsForTimes(timestampsToSearch).forEach((k, v) -> {
                    consumer.seek(k, v.offset());
                });
            }
        }
    }

我确定所有新订阅中的最早时间戳,将这些订阅标记为不是新订阅,然后使用消费者在主题上寻找最早的时间戳。

为了获取容器空闲事件,必须在容器属性中配置空闲间隔,如here所述。

然后,KafkaListener将负责将旧事件发送给(以前是新的)订阅者:

    @KafkaListener(id = "qux", topics = { "${app.event.topic}" }, errorHandler = "kafkaListenerErrorHandler")
    public void receive(@Payload Event event, @Headers MessageHeaders headers) throws JsonProcessingException {

        // collect the subscribers not marked as new
        Collection<EventListenerSubscription> oldSubscriptions = 
                subscriptions.stream().filter(s -> !s.isNew())
                .collect(Collectors.toList());

        for (EventListenerSubscription s : oldSubscriptions) {
            if (s.getLastTimeStamp() < timestamp) {
                s.addMessage(event, timestamp);
            }
        }
    }