我最近开始与Kafka合作,我正在尝试设置Kafka消费者,他将在生产中收听kafka。我正在阅读Commits
和Offsets
,看起来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
类中的readOffsetFromExternalStore
和OffsetManager
方法:
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;
}
问题:
我还有其他问题,现在我正在管理数据库表中的偏移,而不是像上面的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(主题,分区) );