如何手动管理偏移/提交以避免收到重复的邮件和丢失数据?

时间:2016-12-10 07:19:02

标签: java apache-kafka

我最近开始与Kafka合作,我正在尝试设置Kafka消费者,他将在生产中收听kafka。我正在阅读CommitsOffsets,看起来Kafka提供了一种跟踪群组消费者读取哪些记录的方法。

显然,管理偏移对客户端应用程序有很大影响。自动提交很方便,但它们不能给开发人员足够的控制以避免重复的消息,因此我计划自己手动管理偏移,以便消除丢失消息的可能性并减少重新平衡期间重复的消息数量。

我看到了这个github code,其中通过将偏移量写入磁盘来手动管理偏移量。在我的情况下,我使用相同的想法,但在我的情况下管理数据库表中的偏移量,我将有多个消费者在不同的机器上运行消耗来自同一主题的数据,所以我计划在给定主题的情况下存储偏移量数据并分区到数据库中,以便所有机器都知道如果发生任何重新平衡,该主题/分区的最后一次偏移是什么。

以下是我的消费者代码,其中来自kafka的我poll数据:

  private static final OffsetManager offsetManager = new OffsetManager("storage2");
  private KafkaConsumer<byte[], byte[]> consumer;

  @Override
  protected void run(String consumerName, Properties consumerProps) {
    consumer = new KafkaConsumer<>(consumerProps);
    consumer.subscribe(getTopicsBasedOnConsumerName(), new KafkaRebalanceListener(consumer));

    Map<String, Object> config = new HashMap<>();
    config.put(Config.URLS, TEST_URL);
    GenericRecordDomainDataDecoder decoder = new GenericRecordDomainDataDecoder(config);

    while (true) {
      ConsumerRecords<byte[], byte[]> records = consumer.poll(Long.MAX_VALUE);
      for (ConsumerRecord<byte[], byte[]> record : records) {
        GenericRecord payload = decoder.decode(record.value());
        // extract data from payload
        // save all these data and then manually commit offsets as below

        offsetManager.saveOffsetInExternalStore(record.topic(), record.partition(),
            record.offset());           
      }
    }
  }

以下是我的KafkaRebalanceListener课程:

public class KafkaRebalanceListener implements ConsumerRebalanceListener {
  private final OffsetManager offsetManager = new OffsetManager("storage2");
  private final Consumer<byte[], byte[]> consumer;

  public KafkaRebalanceListener(KafkaConsumer<byte[], byte[]> consumer) {
    this.consumer = consumer;
  }

  @Override
  public void onPartitionsAssigned(Collection<TopicPartition> partitions) {
    for (TopicPartition partition : partitions) {
      offsetManager.saveOffsetInExternalStore(partition.topic(), partition.partition(),
          consumer.position(partition));
    }
  }

  @Override
  public void onPartitionsRevoked(Collection<TopicPartition> partitions) {
    for (TopicPartition partition : partitions) {
      consumer.seek(partition,
          offsetManager.readOffsetFromExternalStore(partition.topic(), partition.partition()));
    }
  }
}

以下是saveOffsetInExternalStore类中的readOffsetFromExternalStoreOffsetManager方法:

public void saveOffsetInExternalStore(String topic, int partition, long offset) {
    String sql = "insert into manage_offset (topic, partition, offset) values (?, ?, ?)";
    // insert/update offset given a topic and partition
    // my primary key in the table is "topic + partition"
}

// returning offset + 1
public long readOffsetFromExternalStore(String topic, int partition) {
    String sql = "select * from manage_offset where topic=? and partition=?";
    try {
        // return offset from the table given a topic and partition if we finds it
        long offset = row.getLong("offset");
        return offset + 1;
    } catch (Exception ex) {
      // log error
    }
    // else return 0
    return 0;
}

问题:

  1. 我们用这个&#34;确切一次的场景&#34;带来的真正好处是什么?与启用自动提交相比?在什么情况和情况下,这将有所帮助。我希望消除丢失消息的可能性,并减少重新平衡期间重复的消息数量,这就是我考虑采用这种方法的原因。虽然它似乎经常可行,但我想它永远不应该依赖,因为总有一些警告。即使采用这种模式,消费者也无法保证在失败后不会重新处理消息。如果消费者使用消息然后在将偏移量刷新到磁盘或写入数据库之前失败怎么办?如果在处理消息之前写入磁盘/数据库,如果编写偏移量然后在实际处理消息之前失败怎么办?即使您在每条消息之后向ZooKeeper提交偏移量,也会存在同样的问题。
  2. 我还有其他问题,现在我正在管理数据库表中的偏移,而不是像上面的github代码那样写入每台机器的磁盘。在我的情况下Bcoz我将有多个消费者在不同的机器上运行,因此我认为管理磁盘上的偏移将是一个问题。让我们说如果我们有3个消费者(C1,C2和C3,每个消费者在不同的机器上运行)消耗一个带有三个分区(P1,P2,P3)的主题,那么每个消费者将被分配一个分区,他们将消耗来自他们的数据。例如:C1正在处理P1,C2正在处理P2而C3正在处理P3所有在不同的机器上。并且根据github代码,它将继续向该机器磁盘写入偏移量,但是如果一台机器(C1发生故障)发生故障,现在我们有两台机器,那么让我们说在重新平衡之后C2会也可以在P1和P2上工作,但C2消费者/机器对P1分区的最后一个偏移量一无所知,所以它会得到0?因此,为了解决这种情况,我应该使用集中式数据库,其中所有机器在给定主题和分区的情况下写入/更新偏移量并在给定主题和分区到数据库表中时读取偏移量?你们认为这是可行的方法吗?在我的情况下Bcoz,我将有多个消费者在不同的机器上运行,消耗来自同一主题的数据,并且我计划将给定主题和分区的偏移数据存储到数据库中,以便所有机器知道什么是最后的偏移量。那个主题/分区是否发生任何重新平衡?

    CREATE TABLE manage_offset(   主题文字,   分区int,   抵消bigint,   PRIMARY KEY(主题,分区) );

0 个答案:

没有答案