Kafka的Offset管理心得

    1. Kafka更新历史

        对kafka版本的理解,普遍分为了两个大版本:0.9版本之前,0.9版本之后(包含)。两个大版本对offset的存储管理有很大的改革。0.9+之后的版本,将offset的存储管理迁移到了kafka内部,减轻zk对offset频繁的维护带来的压力。

Versionoffset存储位置
0.9之前zk:/consumer/groupid/topic/partition
0.9之后(包含)

kafka topic:__consumer_offsets

也可兼容 zk 存储offset

    2. 自定义offset存储

        在实际开发中,不免会碰到对offset的自定义存储,使用较多的是借助MySQL, es, redis等第三方组件进行存储管理。最近的个人学习中,自己采用了较为常用便捷的MySQL进行存储。

1. 创建 custom_offsets 表,用于存储 offset 信息

-- custom_offsets definition

CREATE TABLE `custom_offsets` (
  `group_id` varchar(100) NOT NULL,
  `topic_name` varchar(100) NOT NULL,
  `partition_id` int(10) NOT NULL,
  `offset` int(10) NOT NULL,
  `create_time` varchar(100) NOT NULL DEFAULT '1990-01-01 00:00:00',
  `update_time` varchar(100) NOT NULL DEFAULT '1990-01-01 00:00:00',
  PRIMARY KEY (`group_id`,`topic_name`,`partition_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

        表格创建后,开始代码的处理。

2. 自定存储offset的关键是需要自定义一个 ConsumerRebalanceListener 类

    使用默认的消费者代码时,我们简单的如下API即可:

/** 指定需要消费的 topic */
Set<String> topics = new HashSet<>();
topics.add(topicName);
consumer.subscribe(topics);

   consumer.subscribe(Collection<String> topics),看底层代码会发现,其实是调用了consumer.subscribe(Collection<String> topics, new NoConsumerRebalanceListener())方法。NoConsumerRebalanceListener类实现了ConsumerRebalanceListener接口,但是没有做任何的逻辑处理。阅读ConsumerRebalanceListener接口的注释,会发现这是一个回调接口,可以触发自定义offset的分配方式。因此,需要创建一个实现ConsumerRebalanceListener接口的自定义类,来进行offset的分配管理。如下是个人的简单实现,供大家借鉴:

package com.private.kafkademo;

import com.private.utils.DBUtils;
import org.apache.kafka.clients.consumer.Consumer;
import org.apache.kafka.clients.consumer.ConsumerRebalanceListener;
import org.apache.kafka.common.TopicPartition;

import java.sql.SQLException;
import java.util.Collection;
import java.util.Iterator;

/**
 * Author  Wan
 * Date    2021-08-10
 * Version 1.0
 * Description : 自定义存储 kafka 消费的 offset 到 mysql
 */
public class CustomRebalanceListener implements ConsumerRebalanceListener {
    private Consumer consumer;
    private String groupId;
    private String topicName;
    private DBUtils dbUtils;

    public CustomRebalanceListener(Consumer consumer, String groupId, String topicName, DBUtils dbUtils) {
        this.consumer = consumer;
        this.groupId = groupId;
        this.topicName = topicName;
        this.dbUtils = dbUtils;
    }

    @Override
    public void onPartitionsRevoked(Collection<TopicPartition> partitions) {

        System.out.println("Store offset to mysql ...");

        Iterator<TopicPartition> iterator = partitions.iterator();
        while (iterator.hasNext()) {
            TopicPartition next = iterator.next();
            int partitionId = next.partition();
            long offset = consumer.position(next);

            CustomOffset customOffset = new CustomOffset(groupId, topicName, partitionId, offset);

            try {
                dbUtils.insertDB(customOffset);
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }

    }

    @Override
    public void onPartitionsAssigned(Collection<TopicPartition> partitions) {

        System.out.println("Custom assigned offset ...");

        Iterator<TopicPartition> iterator = partitions.iterator();
        while (iterator.hasNext()) {
            TopicPartition next = iterator.next();
            int partition = next.partition();

            long offset = 0L;

            try {
                offset = dbUtils.queryOffset(groupId, topicName, partition);
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }

            consumer.seek(next, offset);
        }

    }
}

        onPartitionsRevoked(Collection<TopicPartition> partitions) : 这个回调函数的作用是,将当前的 TopicPartition 信息对应的offset进行存储

        onPartitionsAssigned(Collection<TopicPartition> partitions) : 这个回调函数的作用是,从存储容器中取出当前 TopicPartition 信息对应的 offset 进行重分配

       完成了这步之后,可以使用 subscribe(Collection<String> topics, ConsumerRebalanceListener listener) 方法订阅所需要进行消费的 topic。

/** 指定需要消费的 topic */
Set<String> topics = new HashSet<>();
topics.add(topicName);
consumer.subscribe(topics, new CustomRebalanceListener(consumer, groupId, topicName, dbUtils));

正常消费后可以从mysql中读取到当前消费者消费到的 offset

         3. 总结

        自定存储offset,1. 可以很直观的看到数据消费的情况;2. 可以通过操作mysql,从而达到对kafka的消息进行从指定的offset开始进行重新消费;3. offset信息不再存储到 topic:__consumer_offsets下,减少了对kafka的读写操作。

综上都是个人的一些理解,如有不正确的地方,可留言私信,相互探讨,感谢~~~

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Flink Kafka Offset 管理是指在使用 Flink 消费 Kafka 数据流时,如何管理消费的偏移量(offset),以便在出现故障或重新启动应用程序时能够准确地从上次的偏移量继续消费数据。 Flink 支持两种方式来管理 Kafka 偏移量:手动管理和自动管理。 1. 手动管理: 在手动管理模式下,应用程序需要自行跟踪和存储偏移量,并在恢复或重启时将偏移量手动设置为上次记录的偏移量。常见的做法是将偏移量保存在外部存储中,例如 Apache ZooKeeper 或存储系统等。这种方式需要编写一些代码来实现偏移量的跟踪和管理。手动管理模式有较高的灵活性,但需要开发者自行处理更多的细节。 2. 自动管理: 自动管理模式下,Flink 提供了与 Kafka 进行整合的功能,可以自动跟踪和管理每个 Kafka 分区的偏移量。Flink 的 Kafka Connector 提供了可靠的偏移量管理功能,能够自动订阅 Kafka 的特定主题和分区,并在每次消费数据时自动跟踪和记录偏移量。如果应用程序在故障之后重新启动,Flink 会读取最近的保存的偏移量,并从偏移量所在位置继续消费数据。自动管理模式相对简单方便,无需编写额外的代码。 两种模式各有优缺点,选择适合自己应用场景的方式进行偏移量管理。 总之,Flink Kafka Offset 管理是确保在 Flink 应用程序消费 Kafka 数据流时,能够准确地从上次消费的偏移量继续消费数据的一种机制,开发者可以选择手动管理或自动管理模式来实现偏移量的跟踪和管理

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值