30 | 怎么重设消费者组位移?

本文详细介绍了Kafka消费者组位移重置的原因及策略,包括Earliest、Latest、Current、Specified-Offset、Shift-By-N、DateTime和Duration等,并提供了API和命令行两种方式的实现细节,帮助理解如何管理和监控Kafka消费者的行为。
摘要由CSDN通过智能技术生成


Kafka 核心技术与实战

管理与监控

30 | 怎么重设消费者组位移?

为什么要重设消费者组位移?

Kafka 和传统的消息引擎在设计上是有很大区别的,其中一个比较显著的区别就是,Kafka 的消费者读取消息是可以重演的(replayable)。

像 RabbitMQ 或 ActiveMQ 这样的传统消息中间件,它们处理和响应消息的方式是破坏性的(destructive),即一旦消息被成功处理,就会被从 Broker 上删除。

由于 Kafka 是基于日志结构(log-based)的消息引擎,消费者在消费消息时,仅仅是从磁盘文件上读取数据而已,是只读的操作,因此消费者不会删除消息数据。同时,由于位移数据是由消费者控制的,因此它能够很容易地修改位移的值,实现重复消费历史数据的功能。

如何确定是使用传统的消息中间件,还是使用 Kafka 呢?

如果应用场景中,消息处理逻辑非常复杂,处理代价很高,同时又不关心消息之间的顺序,那么传统的消息中间件是比较合适的;反之,如果应用场景需要较高的吞吐量,但每条消息的处理时间很短,同时又很在意消息的顺序,此时,Kafka 是为首选。

重设位移策略

不论是哪种设置方式,重设位移大致可以从两个维度来进行。

  • 位移维度。 这是指根据位移值来重设。也就是说,直接把消费者的位移值重设成给定的位移值。
  • 时间维度。 可以给定一个时间,让消费者把位移调整成大于该时间的最小位移;也可以给出一段时间间隔,比如 30 分钟前,然后让消费者直接将位移调回 30 分钟之前的位移值。

在这里插入图片描述

Earliest 策略 表示将位移调整到主题当前最早位移处。这个最早位移不一定就是 0,因为在生产环境中,很久远的消息会被 Kafka 自动删除,所以当前最早位移很可能是一个大于 0 的值。如果想要重新消费主题的所有消息,那么可以使用 Earliest 策略。

Latest 策略 表示把位移重设成最新末端位移。如果总共向某个主题发送了 15 条消息,那么最新末端位移就是 15。如果想跳过所有历史消息,从最新的消息处开始消费的话,可以使用 Latest 策略。

Current 策略 表示将位移调整成消费者当前提交的最新位移。比如说,修改了消费者程序代码,并重启了消费者,结果发现代码有问题,需要回滚之前的代码变更,且位移没有提交(若位移提交了,就不能用 current),就可以把位移重设到消费者重启时的位置。

Specified-Offset 策略 是比较通用的策略,表示消费者把位移值调整到指定的位移处。这个策略的典型使用场景是,消费者程序在处理某条错误消息时,可以手动地“跳过”此消息的处理。在实际使用过程中,可能会出现 corrupted 消息无法被消费的情形,此时消费者程序会抛出异常,无法继续工作。一旦碰到这个问题,可以尝试使用 Specified-Offset 策略来规避。

Shift-By-N 策略 指定的是位移的相对数值,即给出要跳过的一段消息的距离即可。这里的“跳”是双向的,既可以向前“跳”,也可以向后“跳”。

DateTime 策略 允许指定一个时间,然后将位移重置到该时间之后的最早位移处。常见的使用场景是,想重新消费昨天的数据,那么可以使用该策略重设位移到昨天 0 点。

Duration 策略 是指给定相对的时间间隔,然后将位移调整到距离当前给定时间间隔的位移处。具体格式是 PnDTnHnMnS(以字母 P 开头,后面由 4 部分组成,即 D、H、M 和 S,分别表示天、小时、分钟和秒)。

目前,重设消费者组位移的方式有两种。

  • 通过消费者 API 来实现。
  • 通过 kafka-consumer-groups 命令行脚本来实现。
消费者 API 方式设置

通过 Java API 的方式来重设位移,需要调用 KafkaConsumer 的 seek 方法,或者是它的变种方法 seekToBeginning 和 seekToEnd。

Earliest 策略和 seekToBeginning 方法搭配,Latest 策略和 seekToEnd 方法搭配,其他策略和 seek 方法搭配。

void seek(TopicPartition partition, long offset);
void seek(TopicPartition partition, OffsetAndMetadata offsetAndMetadata);
void seekToBeginning(Collection<TopicPartition> partitions);
void seekToEnd(Collection<TopicPartition> partitions);

Earliest 策略的实现代码如下:

Properties consumerProperties = new Properties();
consumerProperties.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, false);
consumerProperties.put(ConsumerConfig.GROUP_ID_CONFIG, groupID);
consumerProperties.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest"); // Earliest 策略
consumerProperties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
consumerProperties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
consumerProperties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, brokerList);

String topic = "test";  // 要重设位移的 Kafka 主题 
try (final KafkaConsumer<String, String> consumer = 
  new KafkaConsumer<>(consumerProperties)) {
         consumer.subscribe(Collections.singleton(topic));
         consumer.poll(0);
         consumer.seekToBeginning(
  consumer.partitionsFor(topic).stream().map(partitionInfo ->          
  new TopicPartition(topic, partitionInfo.partition()))
  .collect(Collectors.toList()));
} 

Latest 策略和 Earliest 是类似的,只需要使用 seekToEnd 方法即可,如下面的代码所示:

consumer.seekToEnd(
  consumer.partitionsFor(topic).stream().map(partitionInfo ->          
  new TopicPartition(topic, partitionInfo.partition()))
  .collect(Collectors.toList()));

实现 Current 策略,需要借助 KafkaConsumer 的 committed 方法来获取当前提交的最新位移,代码如下:

consumer.partitionsFor(topic).stream().map(info -> 
  new TopicPartition(topic, info.partition()))
  .forEach(tp -> {
  long committedOffset = consumer.committed(tp).offset();
  consumer.seek(tp, committedOffset);
});

实现 Specified-Offset 策略,直接调用 seek 方法即可,如下所示:

long targetOffset = 1234L;
for (PartitionInfo info : consumer.partitionsFor(topic)) {
  TopicPartition tp = new TopicPartition(topic, info.partition());
  consumer.seek(tp, targetOffset);
}

实现 Shift-By-N 策略,主体代码逻辑如下:

for (PartitionInfo info : consumer.partitionsFor(topic)) {
         TopicPartition tp = new TopicPartition(topic, info.partition());
  // 假设向前跳 123 条消息
         long targetOffset = consumer.committed(tp).offset() + 123L; 
         consumer.seek(tp, targetOffset);
}

实现 DateTime 策略,需要借助 KafkaConsumer. offsetsForTimes 方法。假设要重设位移到 2022 年 4 月 4 日晚上 8 点,那么具体代码如下:

long ts = LocalDateTime.of(
  2022, 4, 4, 20, 0).toInstant(ZoneOffset.ofHours(8)).toEpochMilli();
Map<TopicPartition, Long> timeToSearch = 
         consumer.partitionsFor(topic).stream().map(info -> 
  new TopicPartition(topic, info.partition()))
  .collect(Collectors.toMap(Function.identity(), tp -> ts));

for (Map.Entry<TopicPartition, OffsetAndTimestamp> entry : 
  consumer.offsetsForTimes(timeToSearch).entrySet()) {
consumer.seek(entry.getKey(), entry.getValue().offset());
}

实现 Duration 策略。假设要将位移调回 30 分钟前,代码如下:

Map<TopicPartition, Long> timeToSearch = consumer.partitionsFor(topic).stream()
         .map(info -> new TopicPartition(topic, info.partition()))
         .collect(Collectors.toMap(Function.identity(), tp -> System.currentTimeMillis() - 30 * 1000  * 60));

for (Map.Entry<TopicPartition, OffsetAndTimestamp> entry : 
         consumer.offsetsForTimes(timeToSearch).entrySet()) {
         consumer.seek(entry.getKey(), entry.getValue().offset());
}
命令行方式设置

位移重设还有另一个重要的途径:通过 kafka-consumer-groups 脚本,这个功能是在 Kafka 0.11 版本中新引入的。比起 API 的方式,用命令行重设位移要简单得多。

Earliest 策略直接指定 --to-earliest

bin/kafka-consumer-groups.sh --bootstrap-server kafka-host:port 
--group test-group --reset-offsets --all-topics --to-earliest –execute

Latest 策略直接指定 --to-latest

bin/kafka-consumer-groups.sh --bootstrap-server kafka-host:port 
--group test-group --reset-offsets --all-topics --to-latest --execute

Current 策略直接指定 --to-current

bin/kafka-consumer-groups.sh --bootstrap-server kafka-host:port 
--group test-group --reset-offsets --all-topics --to-current --execute

Specified-Offset 策略直接指定 --to-offset

bin/kafka-consumer-groups.sh --bootstrap-server kafka-host:port 
--group test-group --reset-offsets --all-topics --to-offset <offset> --execute

Shift-By-N 策略直接指定 --shift-by N

bin/kafka-consumer-groups.sh --bootstrap-server kafka-host:port 
--group test-group --reset-offsets --shift-by <offset_N> --execute

DateTime 策略直接指定 --to-datetime

bin/kafka-consumer-groups.sh --bootstrap-server kafka-host:port 
--group test-group --reset-offsets --to-datetime 2019-06-20T20:00:00.000 --execute

Duration 策略直接指定 --by-duration

bin/kafka-consumer-groups.sh --bootstrap-server kafka-host:port 
--group test-group --reset-offsets --by-duration PT0H30M0S --execute
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

久违の欢喜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值