【Kafka】Offset存储机制、__consumer_offsets

1. 为什么需要存储Offset

由于消费者在消费消息的时候可能会由于各种原因而断开消费,当重新启动消费者时我们需要让它接着上次消费的位置offset继续消费,因此消费者需要实时的记录自己以及消费的位置。

在0.90版本之前,这个信息是记录在zookeeper内的,在0.90之后的版本,offset保存在__consumer_offsets 这个topic内。

每个consumer会定期将自己消费分区的offset提交给kafka内部topic:__consumer_offsets,提交过去的时候,key是consumerGroupId+topic+分区号,value就是当前offset的值,kafka会定期清理topic里的消息,最后就保留最新的那条数据
因为__consumer_offsets可能会接收高并发的请求,kafka默认给其分配50个分区(可以通过offsets.topic.num.partitions设置),这样可以通过加机器的方式抗大并发。

2. __consumer_offsets

__consumer_offsets 是 kafka 自行创建的,和普通的 topic 相同。它存在的目的之一就是保存 consumer 提交的位移。

__consumer_offsets 就是系统自动帮我们创建的隐藏的主题,既然是主题,就可以通过创建多个分区提高访问性能,kafka 默认为该 topic 创建了50个分区。

__consumer_offsets 的每条消息格式大致如图所示:

在这里插入图片描述
可以想象成一个 KV 格式的消息,key 就是一个三元组:group.id+topic+分区号,而 value 就是 offset 的值。

考虑到一个 kafka 生成环境中可能有很多 consumer 和 consumer group,如果这些 consumer 同时提交位移,则必将加重 __consumer_offsets 的写入负载,因此 kafka 默认为该 topic 创建了50个分区,并且对每个 group.id 做哈希求模运算,从而将负载分散到不同的 __consumer_offsets 分区上

一般情况下,当集群中第一次有消费者消费消息时会自动创建 __consumer_offsets,它的副本因子受 offsets.topic.replication.factor 参数的约束,默认值为3(注意:该参数的使用限制在0.11.0.0版本发生变化),分区数可以通过 offsets.topic.num.partitions 参数设置,默认值为50。

2.1 OffsetCommitRequest

客户端提交消费位移是使用 OffsetCommitRequest请求实现的,OffsetCommitRequest 的结构如下图所示:
在这里插入图片描述
请求体第一层中的 group_id、generation_id 和 member_id 表示消费者具体信息, retention_time 表示当前提交的消费位移所能保留的时长,不过对于消费者而言这个值置为1。也就是说,按照 broker 端的配置 offsets.retention.minutes 来确定保留时长,默认为10080,即7天,超过这个时间后消费位移的信息就会被删除(使用墓碑消息和日志压缩策略)。
注意:这个参数在2.0.0版本之前的默认值为1440,即1天。

OffsetCommitRequest 中的其余字段大抵也是按照分区的粒度来划分消费位移的,注意还有一个 metadata 字段。metadata 是自定义的元数据信息,如果不指定这个参数,那么就会被设置为空字符串,注意 metadata 的长度不能超过 broker 端参数 offset.metadata.max.bytes 参数所配置的大小,默认值为4096。

2.1.1 Key 和 Value

同消费组的元数据信息一样,最终提交的消费位移也会以消息的形式发送至 __consumer_offsets,与消费位移对应的消息只定义了 key和 value 字段的具体内容,它不依赖于具体版本的消息格式,以此做到与具体的消息格式无关。

下图中展示了消费位移对应的消息内容格式,上面是消息的 key,下面是消息的 value。可以看到 key 和 value 中都包含了 version 字段,这个用来标识具体的 key 和 value 的版本信息,不同的版本对应的内容格式可能并不相同。到当前版本版本(2.x)而言 key 和 value 的 version 值都为1。
在这里插入图片描述

Key
key 中除了 version 字段还有 group、 topic 、 partition 字段,分别表示消费组的 groupId、topic 和 partition 编号。虽然 key 中包含了4个字段,但最终确定这条消息所要存储的分区还是根据单独的 group 字段来计算的,这样就可以保证消费位移信息与消费组对应的 GroupCoordinator 处于同一个 broker 节点上,省去了中间轮转的开销,这一点与消费组的元数据信息的存储是一样的。

Value
value 中包含了5个字段,除 version 字段外,其余的 offset、metadata、commit_timestamp、expire_timestamp 字段分别表示消费位移、自定义的元数据信息、位移提交到 Kafka 的时间戳、消费位移被判定为超时的时间戳。其中 offset 和 metadata 与 OffsetCommitRequest 请求体中的offset 和metadata 对应,而 commit_timestamp 和 OffsetCommitRequest 请求体中的 retention_time 也有关联,commit_timestamp 值与 offsets.retention.minutes 参数值之和即为 expire_timestamp (默认情况下)。

2.2 OffsetCommitResponse

在这里插入图片描述

2.3 查看__consumer_offsets中的消息

可以通过 kafka-console-consumer.sh 脚本来查看 __consumer_offsets 中的内容,不过要设定 --formatter 参数为kafka.coordinator.group.GroupMetadataManager$OffsetsMessageFormatter

注意,该参数在0.11.0版本之前为 kafka.coordinator.GroupMetadataManager$OffsetsMessageFormatter

示例:

./kafka-console-consumer.sh --bootstrap-server 10.163.198.134:9092 --topic __consumer_offsets --partition 35 --from-beginning --formatter 'kafka.coordinator.group.GroupMetadataManager$OffsetsMessageFormatter'

一般情况下, 使用 OffsetsMessageFormatter 打印的格式可以概括为:

"[%s,%s,%d]::[OffsetMetadata[%d,%s],CommitTime %d,ExpirationTime %d]".format(group, topic, partition, offset, metadata, commitTimestamp, expireTimestamp)

如果某个 key( version + group + topic +partition 的组合)对应的消费位移过期了,那么对应的 value 就会被设置为 null,也就是墓碑消息(__consumer_offsets 使用的是日志压缩策略)。对应的打印结果也会变成如下的形式:

"[%s,%s,%d]::NULL".format(group, topic, partition)

当在查看主题 __consumer_offsets 中的内容时出现下面这种情况:
[consumer_test,testTopic,0]::NULL,这说明对应的消费位移己经过期了。

在 Kafka 中有一个名为“delete-expired-group-metadata”的定时任务来负责清理过期的消费位移,这个定时任务的执行周期由参数 offsets.retention.check.interval.ms 控制,默认值为600000,即10分钟。

还有 metadata,一般情况下它的值要么为 null 要么为空字符串,OffsetsMessageFormatter 会把它展示为 NO_METADATA,否则就按实际值进行展示。

3. offset要提交到__consumer_offsets的哪个分区

通过如下公式可以选出consumer消费的offset要提交到__consumer_offsets的哪个分区
公式:hash(consumerGroupId) % __consumer_offsets主题的分区数

参考

__consumer_offsets

  • 6
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值