1. 前言
2. 存储结构
2.1 GROUP_METADATA 消费组元信息
2.2 OFFSET_COMMIT 消费偏移量信息
3. 写入时机
3.1 Group元信息写入时机
3.2 Offset写入时机
在之前的文章中, 我们有讲解过消息是如何从Producer存储到Broker中的, 也分析了消息的结构, 如果不清楚的话,可以看看 [图解Kafka的RecordBatch结构]
那么作为消费者, 消费了某条消息之后又是如何记录我已经消费了这条消息呢?
如果消费者重启了, 那么又是如何获知我上次消费到了哪个消息呢?
既然要解决上述的问题, 那么我们肯定要把我们已经消费的消息给记录下来, 不可能把整个Record给记录下来吧,那也太大了, 但是我们知道消息存储在具体的分区的时候都是有自己的Offset的, 并且是有序的offset, 所以我们只需要记录下当前消费到了哪个Offset就行了。
这个Offset保存在哪里呢?
在 0.9 版本之前,这个信息是记录在zookeeper内的。但是因为频繁的对zookeeper进行读写,压力太大集群不稳 所以在0.9之后的版本,offset保存在__consumer_offsets
这个内部的Topic内了。
那么这个是怎么储存的呢?
因为本质上还是把消费的offset当做一条普通的消息发送到内部的Topic __consumer_offsets
中, 所以存储数据的时候, 也就是存到Record消息结构的 Key Value 中。
根据版本的迭代,存储的Key Value 的Schema也有一些略微不一样, 接下来我会把每个版本的Schema都讲解一下。
值得一提的是, 这个__consumer_offsets
保存的并不仅仅是offset的信息, 实际上它分为两种类型的数据
一种是 GROUP_METADATA
这种类型的数据, 存放的是消费组相关的元数据信息
一种是 OFFSET_COMMIT
这种类型的数据,存放的是就是上面说的Offset消费偏移量数据
接下来我们看看他们的数据结构
我们下面要说的存储结构说的是Record数据结构里面的 Key Value
GROUP_METADATA 存储的是消费组的元信息, 比如groupId 、protocol_type、protocol、leader、members 等等。
比如说有很多的消费组 , 每个消费组有很多消费者客户端member,每个member的 client.id、clien.host等等信息。
这个消息的Key Value数据如下
Key的结构
GroupMetadataKey结构
这里的GroupMetadataKey的最开始的2个字节存储的是version数据
除了version,后面的数据才是Schema, 这个Schema只有一个字段就是 group。
注意: 在数据结构上 version 并不是属于Schema内的。
这里的key就是group.id
GroupMetaDataValue 的数据结构变更的有点多, 有好几次升级, 版本信息存放在version
里面,并且占用2个字节
特别注意的是: version跟其他的并不属于同一个Schema里面。
1 .Verion0 -- 基础版本,也是所有版本的共有属性
这个是最原始的版本,后续的版本都是在这个基础上做加法
Kafka版本:kafak version < 0.10.1
Schema:
这里是每个结构的共有属性, 后面每个版本讲解的时候只会讲差异
属性 | 描述 |
---|---|
protocol_type | 协议类型,Consumer发起的协议是 comsumer , 另一个可选项为 connect |
generation | 消费组协调器的年代标记,从0自增,每发生一次Rebalance就会增长一代 |
protocol | 消费组分区分配策略, 比如range , 假如多个Member都配置很多策略, 那么会选择他们都支持的策略 |
leader | 消费组协调器中总舵Member中的Leader Member, Leader Member有负责根据分区分配策略来计算新的分配方式并返回给组协调器的责任。这个值是Leader MemberId |
members | 消费组下面的所有成员们的元信息,每个元信息的结构请看下面的表格 |
Member结构
属性 | 描述 |
---|---|
member_id | 每个成员的唯一标识 |
client_id | 客户端的ID |
client_host | 客户端host |
session_timeout | 消费者向组协调器定期发送心跳证明自己的存活,如果在这个时间之内broker没收到,那broker就将此消费者从group中移除,进行一次reblance;默认10000(10 秒) |
assignment | member对应的自己分配到的分区数据,具体请看下图SyncGroupRequest请求参数 |
subscription | 这个是Member对应分配策略的订阅的元信息, 包含的数据有 1. 订阅的topics 2. userData自定义数据 3 ownedPartitions;这个userData,需要分区分配策略实现一个方法返回,;owned_partitons :这个记录着当前分配到的分区列表,平时是空列表。具体的结构请看下面JoinGroupRquest请求参数的红框起来的部分 |
SyncGroupRequest请求参数assignment属性的结构看下图虚线部分的assignment
private List<TopicPartition> partitions;
private ByteBuffer userData;
JoinGroupRquest请求参数
subscription属性的结构看下图红线框起来的部分 metadata
2 .Verion1
Kafka版本:0.10.1
< kafka version <2.1
Schema:
相比于version = 0 的版本,在member里面新增了一个字段rebalance_timeout
属性 | 描述 |
---|---|
rebalance_timeout | 重平衡的超时时间, 由配置max.poll.interval.ms控制,默认值300000(5 分钟),如果消费者两次poll的时间超过了此值,那就认为此消费者能力不足,将此消费者的commit标记为失败,并将此消费者从group移除,触发一次reblance,将该消费者消费的分区分配给其他人 |
2 .Verion2
Kafka版本:2.1
< kafka version <2.3
Schema:
跟V1的版本区别是 新增了一个字段 current_state_timestamp
属性 | 描述 |
---|---|
current_state_timestamp | 当前Group状态流转的时间,从上一个状态流转到下一个状态的这个时间点。 |
2 .Verion3
Kafka版本:2.3
< kafka version Schema:
跟V2的版本区别是 member里面新增了一个字段 group_instance_id
属性 | 描述 |
---|---|
group_instance_id | 静态成员的实例ID,关于静态成员和动态成员请看 |
这种类型的数据就是跟存储的每个消费组每个分区消费的offset的数据。
version=0或1 表示的是OFFSET_COMMIT类型。
version=2 表示的是上面提过的GROUP_METADATA类型
熟悉 | 描述 |
---|---|
group | 消费组ID, group.id |
topic | 订阅的主题 |
partition | 订阅的分区 |
因为可能会有很多个不同的消费组消费同一个分区, 并且他们直接是相互独立的, 所以为了区别他们,在存储消费组offset的时候, key的值实际上是 由group.id+topic+partition
组成的。
并且一个分区在同一个消费组内只会被其中一个消费者消费, 这样就不会出现多个消费者重复消费的问题了。
Value是存储的消费组的消费偏移量Offset,这个Offset是针对每个group.id+topic+partition
维度的。
除了Offset信息,还存了其他的一些信息, 请看下图Value结构的变更历史
我们主要看Version=3的一个版本
属性 | 描述 |
---|---|
offset | 消费偏移量 |
leader_epoch | Leader纪元,如果当前leader不存在为-1 |
metadata | |
commit_timestamp | 偏移量提交时间 |
元信息的写入源码入口在
GroupMetadataManager#storeGroup
/**
* 公众号:石臻臻的杂货铺
* Vx: shiyanzu001
* 领取20万字《Kafka运维实战宝典》
**/
def storeGroup(group: GroupMetadata,
groupAssignment: Map[String, Array[Byte]],
responseCallback: Errors => Unit): Unit = {
//读取分区数据,获取消息的结构版本
getMagic(partitionFor(group.groupId)) match {
case Some(magicValue) =>
// We always use CREATE_TIME, like the producer. The conversion to LOG_APPEND_TIME (if necessary) happens automatically.
val timestampType = TimestampType.CREATE_TIME
val timestamp = time.milliseconds()
// 获取Group元信息的Key结构
val key = GroupMetadataManager.groupMetadataKey(group.groupId)
// 获取Group元信息的Value结构
val value = GroupMetadataManager.groupMetadataValue(group, groupAssignment, interBrokerProtocolVersion)
//构造消息体
val records = {
val buffer = ByteBuffer.allocate(AbstractRecords.estimateSizeInBytes(magicValue, compressionType,
Seq(new SimpleRecord(timestamp, key, value)).asJava))
val builder = MemoryRecords.builder(buffer, magicValue, compressionType, timestampType, 0L)
builder.append(timestamp, key, value)
builder.build()
}
//构造TopicPartition,消息将会发往这个分区
val groupMetadataPartition = new TopicPartition(Topic.GROUP_METADATA_TOPIC_NAME, partitionFor(group.groupId))
val groupMetadataRecords = Map(groupMetadataPartition -> records)
val generationId = group.generationId
//当日志写入成功之和调用这个回调函数,并将 创建的group插入缓存
def putCacheCallback(responseStatus: Map[TopicPartition, PartitionResponse]): Unit = {
// 省略很多代码..
}
responseCallback(responseError)
}
//将数据写入到Log中
appendForGroup(group, groupMetadataRecords, putCacheCallback)
case None =>
responseCallback(Errors.NOT_COORDINATOR)
None
}
}
这段代码很简单, 就是构造一下GroupMetadata的消息体并写入__consumer_offset
对应的分区中。我们主要还是要来了解一下在什么情况下才会写入
我们看调用链,发现有三个地方调用了它
这个的执行条件是: 当Group当前是Empty的状态,一个Member也没有,那么这个时候写入一个Group元信。
GroupCoordinator#onCompleteJoin
一般SyncGroup请求都是在JoinGroup完成了之后,所有的Member都会向组协调器发送SyncGroup。
但是Leader Member会带上计算好的新的分区分配方案给协调器
所以协调器当发现是Leader Member给自己发送SyncGroup请求之后,他会把相应的元数据给写入到_consumer_offset
中。(因为Group的元信息有变更了)
GroupCoordinator#doSyncGroup
上面的一个入参assignments列表, 是SyncGroup请求带进来的,具体的结构请看下图右边虚线框;
上图虚线内的单个assignment的数据结构是
private List<TopicPartition> partitions;
private ByteBuffer userData;
想了解 SyncGroupRequest请看:KafkaConsumer SyncGroupRequest详解
在静态成员JoinGroup的时候, 会走单独的静态成员更新流程
GroupCoordinator#updateStaticMemberAndRebalance
判断group的下一代的selectedProtocol(所有memmber都支持并排序最靠前)是否会改变
就是这里的2的情况,去更新一下静态成员的相关信息,主要就是更新 memberId,其他的都没有什么好变更的。分配方案也不会有改变。
GROUP_METADATA_VALUE_SCHEMA版本选择
GroupMetadataManager#groupMetadataValue
def groupMetadataValue(groupMetadata: GroupMetadata,
assignment: Map[String, Array[Byte]],
apiVersion: ApiVersion): Array[Byte] = {
val (version, value) = {
if (apiVersion < KAFKA_0_10_1_IV0)
(0.toShort, new Struct(GROUP_METADATA_VALUE_SCHEMA_V0))
else if (apiVersion < KAFKA_2_1_IV0)
(1.toShort, new Struct(GROUP_METADATA_VALUE_SCHEMA_V1))
else if (apiVersion < KAFKA_2_3_IV0)
(2.toShort, new Struct(GROUP_METADATA_VALUE_SCHEMA_V2))
else
(3.toShort, new Struct(GROUP_METADATA_VALUE_SCHEMA_V3))
}
// 省略。。。
handleJoinGroupRequest
(略)
本文由哈喽比特于2年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/mxTjj3wTXxdOKwEIP3j-7Q
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。