簡體   English   中英

在 Kafka 中通過重放進行精確一次處理

[英]Exactly-once processing in Kafka with replay

我正在使用 Kafka 進行事件日志/處理。 我正在尋找(盡可能接近)一次處理,同時在分區(重新)分配期間支持“重播”,通知重播的事件處理程序以便它可以重建它 state

這是我的代碼:

private final KafkaConsumer<String, String> consumer;
private final KafkaProducer<String, String> producer;
private final BiFunction<String, Boolean, String> eventHandler;
private final long[] startingCommitOffsets;

public void onParitionsAssigned(Collection<TopicPartition> partitions) {
  partitions.forEach(p -> startingCommitOffsets[p.partition()] = consumer.position(p));
  consumer.seekToBeginning(partitions);
}

public void run() {
  while (true) {
    var records = consumer.poll(Duration.ofMillis(Long.MAX_VALUE));
    var commitRecords = new HashMap<TopicPartition, OffsetAndMetadata>();

    producer.beginTransation();

    records.forEach(r -> {
      var isReplay = r.offset() < startingCommitOffsets[r.partition()];
      var resultEvent = eventHandler.apply(r.value(), isReplay);
      producer.send(new ProducerRecord<>(r.topic(), r.key(), resultEvent));

      if (!isReplay) {
        commitRecords.put(new TopicPartition(r.topic(), r.partition(), new OffsetAndMetadata(r.offset()));
      }
    });

    producer.commitTransaction();

    if (!commitRecords.isEmpty()) {
      consumer.commitSync(commitRecords);
    }
  }
}

我的問題:

  1. 分配分區后,我保存當前的 position 並尋找到開頭。 這不會改變已提交的 position 嗎? (文檔不清楚)
  2. product.commitTransaction()consumer.commitSync()是兩個獨立的操作。 如果后者失敗,我們將已經提交了一些新事件,這些新事件將在下次處理事件時重復 - 有沒有辦法將它們組合成一個操作?

分配分區后,我保存當前的 position 並尋找到開頭。 這不會改變已提交的 position 嗎?

提交的 position 不會更改,直到您明確調用commitAsync() or commitSync()auto.commit.enable=true

producer.commitTransaction()consumer.commitSync()是兩個獨立的操作。 如果后者失敗,我們將已經提交了一些新事件,這些新事件將在下次處理事件時復制。有沒有辦法將它們組合成一個操作?

producer.sendOffsetsToTransaction()

此方法可能是您正在尋找的方法,以實現僅一次處理。

文檔中:

將指定偏移量列表發送給消費者組協調器,並將這些偏移量標記為當前事務的一部分。 僅當事務成功提交時,這些偏移量才會被視為已提交。 提交的偏移量應該是您的應用程序將使用的下一條消息,即lastProcessedMessageOffset+1

更重要的是,

請注意,消費者應該具有enable.auto.commit=false並且也不應該手動提交偏移量(通過同步或異步提交)。


您可以通過poll()ConsumerRecord中推斷出TopicPartition和偏移量。

只需將它們( new TopicPartition(record.topic(), record.partition())new OffsetAndMetadata(record.offset()) )存儲在 map 中,並在您想要提交時傳遞它。

下面的代碼片段可以讓你有個想法(參考):

KafkaProducer producer = createKafkaProducer(
  “bootstrap.servers”, “localhost:9092”,
  “transactional.id”, “my-transactional-id”);

producer.initTransactions();

KafkaConsumer consumer = createKafkaConsumer(
  “bootstrap.servers”, “localhost:9092”,
  “group.id”, “my-group-id”,
  "isolation.level", "read_committed");

consumer.subscribe(singleton(“inputTopic”));
    while (true) {
      ConsumerRecords records = consumer.poll(Long.MAX_VALUE);
      producer.beginTransaction();
      Map<TopicPartition, OffsetAndMetadata> map = new LinkedHashMap<>();
      for (ConsumerRecord record : records) {
        producer.send(producerRecord(“outputTopic”, record));
        map.put(new TopicPartition(record.topic(), record.partition()), new OffsetAndMetadata(record.offset()));
      }
      producer.sendOffsetsToTransaction(offsetMap, group);  
      producer.commitTransaction();
    }

發送偏移量后,我們提交它們。

暫無
暫無

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

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