我们正在使用spring-kafka来消耗必须作为服务器发送事件(SSE)转发给前端的消息。
用户登录后,应该可以看到自上次会话以来错过的所有事件。
如以下所述,当前实现使用ConsumerSeekCallback this answer
但是该回调不支持基础KafkaConsumer(KafkaConsumer#offsetForTimes)的offsetForTimes方法。
所以我必须使用seekToBeginning和filter作为时间戳,当有很多消息时会引起问题...
自给定时间戳记以来,是否还有其他方法仅接收消息? 也许是直接使用消费者的安全方法?
答案 0 :(得分:1)
2.0引入了ConsumerAwareRebalanceListener
(当前版本为2.2.2)。
答案 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);
}
}
}