Java 用春天的云流寻找卡夫卡偏移

Java 用春天的云流寻找卡夫卡偏移,java,spring-cloud-stream,spring-kafka,Java,Spring Cloud Stream,Spring Kafka,我有一个事件源服务,它侦听卡夫卡主题并将状态保存在关系数据库中 考虑此服务的适当恢复策略(即如何在灾难恢复场景中恢复数据库),一个选项是在数据库中保存当前偏移量、拍摄快照并从快照恢复。在这种情况下,服务需要在“恢复模式”下启动时寻找偏移量 我正在使用SpringCloudStream,我想知道该框架是否提供了任何机制来寻找偏移量 我意识到恢复的另一个选择是从零开始播放所有事件,但对于我的一些微服务来说,这不是一个理想的选择。如果你说的是灾难,你凭什么认为你可以向DB写入任何内容? 换句话说,您可

我有一个事件源服务,它侦听卡夫卡主题并将状态保存在关系数据库中

考虑此服务的适当恢复策略(即如何在灾难恢复场景中恢复数据库),一个选项是在数据库中保存当前偏移量、拍摄快照并从快照恢复。在这种情况下,服务需要在“恢复模式”下启动时寻找偏移量

我正在使用SpringCloudStream,我想知道该框架是否提供了任何机制来寻找偏移量


我意识到恢复的另一个选择是从零开始播放所有事件,但对于我的一些微服务来说,这不是一个理想的选择。

如果你说的是灾难,你凭什么认为你可以向DB写入任何内容? 换句话说,您可能会在至少一个事件中处理重复数据消除(至少您必须对此进行说明),如果是这样,那么重复数据消除仍然是您必须处理的问题

我理解您对重新播放的担忧(您只是不想从一开始就回复,但您可以存储定期快照,以确保您有相对固定数量的事件需要重新处理/消除重复


也就是说,Kafka保留当前偏移量,因此您可以依靠Kafka的自然事务功能来确保下次启动微服务时,它将从上次未处理(成功)的偏移量开始。

您可以使用KafkaBindingRebalanceListener接口

@Slf4j
@Component
public class KafkaRebalanceListener implements KafkaBindingRebalanceListener {

    @Value("${config.kafka.topics.offsets:null}")
    private String topicOffsets;

    @Override
    public void onPartitionsAssigned(String bindingName, Consumer<?, ?> consumer, Collection<TopicPartition> partitions, boolean initial) {
        if (topicOffsets != null && initial) {
            final Optional<Map<TopicPartition, Long>> offsetsOptional = parseOffset(topicOffsets);
            if (offsetsOptional.isPresent()) {
                final Map<TopicPartition, Long> offsetsMap = offsetsOptional.get();
                partitions.forEach(tp -> {
                    if (offsetsMap.containsKey(tp)) {
                        final Long offset = offsetsMap.get(tp);
                        try {
                            log.info("Seek topic {} partition {} to offset {}", tp.topic(), tp.partition(), offset);
                            consumer.seek(tp, offset);
                        } catch (Exception e) {
                            log.error("Unable to set offset {} for topic {} and partition {}", offset, tp.topic(), tp.partition());
                        }
                    }
                });
            }
        }
    }

    private Optional<Map<TopicPartition, Long>> parseOffset(String offsetParam) {
        if (offsetParam == null || offsetParam.isEmpty()) {
            return Optional.empty();
        }

        return Optional.of(Arrays.stream(offsetParam.split(","))
                .flatMap(slice -> {
                    String[] items = slice.split("\\|");
                    String topic = items[0];
                    return Arrays.stream(Arrays.copyOfRange(items, 1, items.length))
                            .map(r -> {
                                String[] record = r.split(":");
                                int partition = Integer.parseInt(record[0]);
                                long offset = Long.parseLong(record[1]);
                                return new AbstractMap.SimpleEntry<>(new TopicPartition(topic, partition), offset);
                            });
                }).collect(Collectors.toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue)));
    }
}

您可以使用
resetoffset
使用者属性重置为主题的开头。您可以使用
kafka使用者组
命令行工具将使用者分区重置为任意偏移量。尽管您可以使用ad命令行工具,但目前在
…stream
中不支持以编程方式重置为某个任意偏移量d将
使用者
作为
@StreamListener
的参数,并对其执行搜索;您还可以启用空闲容器事件,以便在没有活动时访问
使用者
。Spring Kafka本身提供了其他机制,但它们目前未被
k的…streamThank@GaryRussell公开afka消费群体
工具是重置为特定偏移量的有用选项,尽管我很惊讶它不支持重置为特定分区的偏移量(我们的主题有许多分区)。出于我们的目的,我喜欢将偏移量重置为特定日期时间的想法。当将服务还原到快照数据库时,我们可以简单地将偏移量重置为快照拍摄之前的偏移量,以确保每条消息至少处理一次。这样,代码就尽可能简单,因为我们不需要将偏移量提交到数据库或者通过编程方式寻找偏移量。
>我很惊讶它不支持为特定分区重置偏移量。
我也是;我没有注意到这一点。
String topicOffsets = "topic2|1:100|2:120|3:140,topic3|1:1000|2:1200|3:1400";