kafka的位移

news2024/12/23 6:28:37

文章目录

    • 概要
    • 消费位移
    • __consumer_offsets主题
    • 位移提交

概要

本文主要总结kafka的位移是如何管理的,在broker端如何通过命令行查看到位移信息,并从代码层面总结了位移的提交方式。

消费位移

对于 Kafka 中的分区而言,它的每条消息都有唯一offset ,用来表示消息在分区中对应位置;对于消费者来说,它也有 offset 的概念,消费者使用 offse 来表示消费到分区中某个消息所在的位置。可通过命令行在查看到一个群组,在topic中两者当前的位置
bin/kafka-consumer-groups.sh --bootstrap-server node1:9092 --describe --group kafka-boot

[root@node1 kafka_2.13-3.2.1]# bin/kafka-consumer-groups.sh --bootstrap-server node1:9092 --describe  --group kafka-boot

Consumer group 'kafka-boot' has no active members.

GROUP           TOPIC            PARTITION  CURRENT-OFFSET  LOG-END-OFFSET  LAG             CONSUMER-ID     HOST            CLIENT-ID
kafka-boot      test-error-topic 0          26              26              0               -               -               -
kafka-boot      normal-test      0          23              24              1               -               -               -

这里对offse 做些区分 对于消息在分区中的位置 CURRENT-OFFSET称为“偏移量” 或消息位移;对于消费者消费到的位置,LOG-END-OFFSET称为“位移 ,有时候也会更明确地称之为“消费位移“。

生产者位移跟消费者位移的关系可以用下图来说明:
在这里插入图片描述

总结几个需要注意的点:

  • 分区副本有两种类型
    领导者副本:生产者跟消费者的请求都只会经过领导者副本
    跟随者副本:首领之外的副本,不处理客户端请求,从领导者副本那里通过拉取的方式同步消息
  • 消费位移存储在Zookeeper或Kafka中,新消费者客户端,偏移量存储咋Kafka内部主题 __consumer_offsets
  • 消费者提交的位移是当前消费消息位移的下一个位置,即:lastConsumeedOffset+1

__consumer_offsets主题

Consumer需要向Kafka记录自己的位移数据,这个汇报过程称为提交位移(Committing Offsets)。

老版本 Consumer 的位移是提交到 ZooKeeper 中保存的。当 Consumer 重启后,它能自动从 ZooKeeper 中读取位移数据,从而在上次消费截止的地方继续消费。这种设计使得Kafka Broker 不需要保存位移数据,减少了 Broker 端需要持有的状态空间,因而有利于实现高伸缩性。

但是,ZooKeeper 其实并不适用于这种高频的写操作,Kafka 社区自 0.8.2.x 版本开始推出了全新的位移管理
机制,将 Consumer 的位移数据作为一条条普通的 Kafka 消息,提交到 __consumer_offsets 中。可以这么说,
__consumer_offsets 的主要作用是保存 Kafka 消费者的位移信息。这种方式能够满足高频的写操作。

两个相关参数:
offsets.topic.num.partitions : 设置 __consumer_offsets主题的分区数,默认是50个分区
offsets.topic.replication.factor : 设置__consumer_offsets主题的副本数,默认是3(下载安装的包中此值可能为1 )

当Kafka 集群中的第一个 Consumer 程序启动时,Kafka 会自动创建位移主题

一共有50个分区,那么消费者将位移提交到了哪个分区呢?

通过如下公式可以选出consumer消费的offset要提交到__consumer_offsets的哪个分区,这个分区leader对应的broker
就是这个consumer group的coordinator
公式:Math.abs(groupID.hashCode()) % numPartitions

Kafka 1.0.2及以后提供了kafka_consumer_groups.sh脚本供用户查看consumer信息

1. 创建一个topic,分区数设置为1,副本数设置为1

[root@node1 kafka_2.13-3.2.1]# bin/kafka-topics.sh --bootstrap-server node1:9092 --create --topic test-offset --partitions 1 --replication-factor 1
Created topic test-offset.

[root@node1 kafka_2.13-3.2.1]# bin/kafka-topics.sh --bootstrap-server node1:9092 --describe --topic test-offset
Topic: test-offset      TopicId: in6gxQ5OQS6x9R8V3oJ7AQ PartitionCount: 1       ReplicationFactor: 1    Configs: segment.bytes=1073741824
        Topic: test-offset      Partition: 0    Leader: 0       Replicas: 0     Isr: 0

2. 向主题test-offset中发送消息

[root@node1 kafka_2.13-3.2.1]# bin/kafka-console-producer.sh --broker-list node1:9092 --topic test-offset
>hello

3. 创建一个消费组,并从头开始消费

[root@node1 kafka_2.13-3.2.1]# bin/kafka-console-consumer.sh --bootstrap-server node1:9092  --from-beginning --consumer-property group.id=testOffsetGroup   --topic test-offset
hello

4. 用代码根据上面的公式计算消费组testOffsetGroup提交位移的分区数

@Test
void getCommitOffsetPartitionTest() {
    String groupId = "testOffsetGroup";
    // 运行结果为16
    System.out.println(Math.abs(groupId.hashCode() % 50));
}
  1. 将kafka配置文件consumer.properties中设置exclude.internal.topics=false,并重启服务
    6. 查看主题__consumer_offsets第16分区上的信息,可以看到消费组testOffsetGroup提交的位移确实保存在了16分区上

[root@node1 kafka_2.13-3.2.1]# bin/kafka-console-consumer.sh --topic __consumer_offsets --partition 16 --bootstrap-server node1:9092 --formatter "kafka.coordinator.group.GroupMetadataManager\$OffsetsMessageFormatter" --from-beginning
[testOffsetGroup,test-offset,0]::OffsetAndMetadata(offset=1, leaderEpoch=Optional.empty, metadata=, commitTimestamp=1691896116191, expireTimestamp=None)
[testOffsetGroup,test-offset,0]::OffsetAndMetadata(offset=1, leaderEpoch=Optional.empty, metadata=, commitTimestamp=1691896121189, expireTimestamp=None)
[testOffsetGroup,test-offset,0]::OffsetAndMetadata(offset=1, leaderEpoch=Optional.empty, metadata=, commitTimestamp=1691896126188, expireTimestamp=None)
[testOffsetGroup,test-offset,0]::OffsetAndMetadata(offset=1, leaderEpoch=Optional.empty, metadata=, commitTimestamp=1691896131188, expireTimestamp=None)
[testOffsetGroup,test-offset,0]::OffsetAndMetadata(offset=1, leaderEpoch=Optional.empty, metadata=, commitTimestamp=1691896133573, expireTimestamp=None)
[testOffsetGroup,test-offset,0]::OffsetAndMetadata(offset=1, leaderEpoch=Optional.empty, metadata=, commitTimestamp=1691896162124, expireTimestamp=None)
[testOffsetGroup,test-offset,0]::OffsetAndMetadata(offset=1, leaderEpoch=Optional.empty, metadata=, commitTimestamp=1691896167124, expireTimestamp=None)
[testOffsetGroup,test-offset,0]::OffsetAndMetadata(offset=1, leaderEpoch=Optional.empty, metadata=, commitTimestamp=1691896172123, expireTimestamp=None)
[testOffsetGroup,test-offset,0]::OffsetAndMetadata(offset=1, leaderEpoch=Optional.empty, metadata=, commitTimestamp=1691896177124, expireTimestamp=None)
[testOffsetGroup,test-offset,0]::OffsetAndMetadata(offset=1, leaderEpoch=Optional.empty, metadata=, commitTimestamp=1691896178781, expireTimestamp=None)

从上面也可看出__consumer_offsets topic的每一日志项的格式都是:
[Group, Topic, Partition]::[OffsetMetadata[Offset, Metadata], CommitTime, ExpirationTime]

客户端提交消费位移是使用OffsetCommitRequest 请求实现的,其结构如下
在这里插入图片描述

__consumer_offsets这个主题中的消息格式为KV对,key为[Group, Topic, Partition],value可以简单理解为记录了偏移量;这样的记录方式,使得broker端不需要关系group下有多少个消费者,新增消费者或者减少消费者发生重平衡时,都能准确地定位到对应地分区应该从哪个位置开始消费。

位移提交

鉴于位移提交甚至是位移管理对 Consumer 端的巨大影响,Kafka,特别是KafkaConsumer API,提供了多种提交位移的方法。从用户的角度来说,位移提交分为自动提交和手动提交;从 Consumer 端的角度来说,位移提交分为同步提交和异步提交。

自动提交

自动提交,就是指 Kafka Consumer 在后台默默地为你提交位移
两个重要的参数

  • enable.auto.commit设置是否自动提交位移,默认是true
  • auto.commit.interval.ms:设置自动提交为true时,该参数生效,标识多久提交一次位移,默认5s,
public static void main(String[] args) {
      Map<String, Object> configs = new HashMap<>();
      configs.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "node1:9092");
      configs.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
      configs.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, UserDeserializer.class);
      configs.put(ConsumerConfig.GROUP_ID_CONFIG, "consumer1");
      configs.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");
      configs.put(ConsumerConfig.CLIENT_ID_CONFIG, "con1");
      // 设置偏移量自动提交。自动提交是默认值。这里做示例。
      configs.put("enable.auto.commit", "true");
      // 偏移量自动提交的时间间隔
      configs.put("auto.commit.interval.ms", "2000");
      KafkaConsumer<String, String> consumer = new KafkaConsumer<String,String>(configs);
      consumer.subscribe(Collections.singleton("tp_demo_01"));

      while (true) {
          ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));
          for (ConsumerRecord<String, String> record : records) {
              System.out.println(record.topic()
                      + "\t" + record.partition()
                      + "\t" + record.offset()
                      + "\t" + record.key()
                      + "\t" + record.value());
          }
      }
 }

设置了 enable.auto.commit 为 true,Kafka 会保证在开始调用 poll 方法时,提交上次 poll 返回的所有消息。从顺序上来说,poll 方法的逻辑是先提交上一批消息的位移,再处理下一批消息,因此它能保证不出现消费丢失的情况。但是会出现消息重复消费

在默认情况下,Consumer 每 5 秒自动提交一次位移。现在,我们假设提交位移之后的 3秒发生了 Rebalance 操作。在 Rebalance 之后,所有 Consumer 从上一次提交的位移处继续消费,但该位移已经是 3 秒前的位移数据了,故在Rebalance 发生前 3 秒消费的所有数据都要重新再消费一次。虽然你能够通过减少 auto.commit.interval.ms 的值来提高提交频率,但这么做只能缩小重复消费的时间窗口,不可能完全消除它。这是自动提交机制的一个缺陷。

手动同步提交

开启手动提交位移的方法就是设置enable.auto.commit 为 false。但是,仅仅设置它为 false 还不够,因为你只是告诉
Kafka Consumer 不要自动提交位移而已,你还需要调用相应的 API 手动提交位移。

public static void main(String[] args) {
    Map<String, Object> configs = new HashMap<>();
    configs.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "node1:9092");
    configs.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);

    configs.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, UserDeserializer.class);
    configs.put(ConsumerConfig.GROUP_ID_CONFIG, "consumer1");
    configs.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");
    configs.put(ConsumerConfig.CLIENT_ID_CONFIG, "con1");

   configs.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "false");
    KafkaConsumer<String, String> consumer = new KafkaConsumer<String,String>(configs);
    consumer.subscribe(Collections.singleton("tp_demo_01"));
    while (true) {
        ConsumerRecords<String, String> records =
                consumer.poll(Duration.ofSeconds(1));
        process(records); // 处理消息
        try {
            consumer.commitSync();
        } catch (CommitFailedException e) {
           handle(e); // 处理提交失败异常
        }
    }
}

调用 commitSync() 时,Consumer 程序会处于阻塞状态,直到远端的 Broker 返回提交结果,这个状态才会结束,这样就会影响TPS。

鉴于此问题,还有另外一个提交方式

手动异步提交

public static void main(String[] args) {
   Map<String, Object> configs = new HashMap<>();
   configs.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "node1:9092");
   configs.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);

   configs.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, UserDeserializer.class);
   configs.put(ConsumerConfig.GROUP_ID_CONFIG, "consumer1");
   configs.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");
   configs.put(ConsumerConfig.CLIENT_ID_CONFIG, "con1");
   configs.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "false");
   KafkaConsumer<String, String> consumer = new KafkaConsumer<String,String>(configs);
   while (true) {
       ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));
       process(records); // 处理消息
       consumer.commitAsync((offsets, exception) -> {
           if (exception != null) {
               handle(exception);
           }
       });
   }
}

commitAsync 是否能够替代 commitSync 呢?答案是不能。commitAsync 的问题在于,出现问题时它不会自动重试。因为它是异步操作,倘若提交失败后自动重试,那么它重试时提交的位移值可能早已经“过期”或不是最新值了。因此,异步提交的重试其实没有意义,所以 commitAsync 是不会重试的。

是手动提交,需要将 commitSync 和 commitAsync 组合使用才能到达最理想的效果,原因有两个:

  1. 利用 commitSync 的自动重试来规避那些瞬时错误,比如网络的瞬时抖动,Broker 端 GC 等。这些问题都是短暂的,自动重试通常都会成功。
  2. 不希望程序总处于阻塞状态,影响 TPS。
public static void main(String[] args) {
   Map<String, Object> configs = new HashMap<>();
   configs.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "node1:9092");
   configs.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);

   configs.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, UserDeserializer.class);
   configs.put(ConsumerConfig.GROUP_ID_CONFIG, "consumer1");
   configs.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");
   configs.put(ConsumerConfig.CLIENT_ID_CONFIG, "con1");
   configs.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "false");
   KafkaConsumer<String, String> consumer = new KafkaConsumer<String,
           String>(configs);
   consumer.subscribe(Collections.singleton("tp_demo_01"));
   try {
       while (true) {
           ConsumerRecords<String, String> records =
                   consumer.poll(Duration.ofSeconds(1));
           consumer.commitAsync();
           process(records); // 处理消息
           consumer.commitAsync(); // 异步提交
       }
   } catch (Exception e) {
       handle(e); // 处理异常
   } finally {
       try {
           consumer.commitSync();// 最后一次提交使用同步阻塞式提交
       } finally {
           consumer.close();
       }

   }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/879171.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python爬虫(十二)_XPath与lxml类库

Python学习指南 有同学说&#xff0c;我正则用的不好&#xff0c;处理HTML文档很累&#xff0c;有没有其他的方法&#xff1f; 有&#xff01;那就是XPath,我们可以用先将HTML文档转换成XML文档&#xff0c;然后用XPath查找HTML节点或元素。 什么是XML XML指可扩展标记语言(E…

Spring Boot + Vue3前后端分离实战wiki知识库系统十二--用户管理单点登录开发一...

目标&#xff1a; 在上一次https://www.cnblogs.com/webor2006/p/17533745.html我们已经完成了文档管理的功能模块开发&#xff0c;接下来则开启新模块的学习---用户登录&#xff0c;这块还是有不少知识点值得学习的&#xff0c;先来看一下整体的效果&#xff0c;关于效果官网有…

算法|Day38 动态规划6

完全背包问题&#xff0c;也就是一个物品可以取多次&#xff0c;我们只需要正序遍历背包容量就可以了。因为一维数组是按左边的状态来进行推导&#xff0c;我们正序&#xff0c;那样就会将前一次取的状态带到下一个位置&#xff0c;也就实现了多次取。如果倒序&#xff0c;左边…

麦肯锡发布《2023科技趋势展望报告》,生成式AI、下一代软件开发成为趋势,软件测试如何贴合趋势?

近日&#xff0c;麦肯锡公司发布了《2023科技趋势展望报告》。报告列出了15个趋势&#xff0c;并把他们分为5大类&#xff0c;人工智能革命、构建数字未来、计算和连接的前沿、尖端工程技术和可持续发展。 类别一&#xff1a;人工智能革命 生成式AI 生成型人工智能标志着人工智…

(el-Table)操作(不使用 ts):Element-plus 中Table 表格组件:多选修改成支持单选及表格相关样式的调整

Ⅰ、Element-plus 提供的 Table 表格组件与想要目标情况的对比&#xff1a; 1、Element-plus 提供 Table 组件情况&#xff1a; 其一、Element-ui 自提供的 Table 代码情况为(示例的代码)&#xff1a; // Element-plus 自提供的代码&#xff1a; // 此时是使用了 ts 语言环境…

Windows11中使用OneDrive按Print Screen截屏按键,把截图自动保存到OneDrive中

参考&#xff1a;关于Onedrive 我已经勾选了自动保存屏幕截图 但是我截图之后我的图片并没有上传到onedrive上面 - Microsoft Community 1. 打开Windows 11的设置&#xff0c;可以通过按下Win I键来快速打开设置&#xff1b; 2. 设置--辅助功能--键盘--使用”print Screen“键…

Rust 重载运算符|复数结构的“加减乘除”四则运算

复数 基本概念 复数定义 由实数部分和虚数部分所组成的数&#xff0c;形如a&#xff0b;bi 。 其中a、b为实数&#xff0c;i 为“虚数单位”&#xff0c;i -1&#xff0c;即虚数单位的平方等于-1。 a、b分别叫做复数a&#xff0b;bi的实部和虚部。 当b0时&#xff0c;a&…

codeforces代:

感受思维的美丽&#xff0c;abcde题目的思路是怎么样的&#xff1a; 上蓝 上紫 可以代 &#xff1a;有问题可以评论区 直接问我 也可以q: 639682754

【严重】Smartbi未授权设置Token回调地址获取管理员权限

漏洞描述 Smartbi是一款商业智能应用&#xff0c;提供了数据集成、分析、可视化等功能&#xff0c;帮助用户理解和使用他们的数据进行决策。 在 Smartbi 受影响版本中存在Token回调地址漏洞&#xff0c;未授权的攻击者可以通过向目标系统发送POST请求/smartbix/api/monitor/s…

python_PyQt5运行股票研究python方法工具V1.0

写在前面&#xff1a; 1 在写研究方法过程中&#xff08;例如&#xff1a;股票研究&#xff09;&#xff0c;很多方法根据数据的更新需要重复运行获取新的结果&#xff0c;本工具就是固化这些需要重复运行的代码&#xff0c;可以直接在工具中运行得到更新的结果。 2 本文是V1…

并发编程系列-Semaphore

Semaphore&#xff0c;如今通常被翻译为"信号量"&#xff0c;过去也曾被翻译为"信号灯"&#xff0c;因为类似于现实生活中的红绿灯&#xff0c;车辆是否能通行取决于是否是绿灯。同样&#xff0c;在编程世界中&#xff0c;线程是否能执行取决于信号量是否允…

DevOps?自动化运维!

by: 雪月三十 DevOps流程图 DevOps介绍 命名 DevOps是Dev和Ops的结合 Dev&#xff08;developer开发&#xff09; Ops&#xff08;operation运维&#xff09; 矛盾 在企业中dev和ops是有一种天然的矛盾&#xff0c;dev要求的是快速迭代&#xff0c;给公司挖掘出商业的价值…

c++实现多进程执行多个shell脚本

一 参考 system()、exec()、fork()三个与进程有关的函数的比较 - 青儿哥哥 - 博客园 (cnblogs.com)https://www.cnblogs.com/qingergege/p/6601807.htmlfork执行流程分析_fork子进程和父进程执行顺序_cytf的博客-CSDN博客https://blog.csdn.net/qq_27420299/articl

IE实验-Qos教学实验

S1 diffserv domain boss //创建差分服务域 8021p-inbound 0 phb ef green //将外部优先级为0的流量&#xff0c;映射到内部优先级ef interface GigabitEthernet0/0/2 trust upstream boss //在接口下调用查分服务域 trust dscp override qos map-table dscp-dscp acl …

【新品发布】ChatWork企业知识库系统源码

系统简介 基于前后端分离架构以及Vue3、uni-app、ThinkPHP6.x、PostgreSQL、pgvector技术栈开发&#xff0c;包含PC端、H5端。 ChatWork支持问答式和文档式知识库&#xff0c;能够导入txt、doc、docx、pdf、md等多种格式文档。 导入数据完成向量化训练后&#xff0c;用户提问…

小米发布会:雷军成长故事与创新壮举,AI大模型技术引领未来,雷军探索之路之从创业波折到小米AI领航,成就高端化传奇!

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

基于物理场的动态模式分解(piDMD)研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

翻转二叉树

声明 该系列文章仅仅展示个人的解题思路和分析过程&#xff0c;并非一定是优质题解&#xff0c;重要的是通过分析和解决问题能让我们逐渐熟练和成长&#xff0c;从新手到大佬离不开一个磨练的过程&#xff0c;加油&#xff01; 原题链接 翻转二叉树备战技术面试&#xff1f;…

shell之正则表达式及三剑客grep命令

一、正则表达式概述 什么是正则表达式&#xff1f; 正则表达式是一种描述字符串匹配规则的重要工具 1、正则表达式定义: 正则表达式&#xff0c;又称正规表达式、常规表达式 使用字符串描述、匹配一系列符合某个规则的字符串 正则表达式 普通字符&#xff1a; 大小写字母…

LED全彩显示屏的前后维护

LED全彩显示屏的维护问题成为一个备受关注的难题。特别是对于采用镶嵌式或壁挂式安装的用户而言&#xff0c;检修和维护工作变得异常繁琐&#xff0c;仿佛一举一动都会影响整体运行。在这一背景下&#xff0c;LED全彩显示屏不仅需要符合严格的标准化要求&#xff0c;更需要具备…