一、消费组管理:消费者组 (Consumer Group)
### --- 什么是消费者组
~~~ consumer group是kafka提供的可扩展且具有容错性的消费者机制。
### --- 三个特性:
~~~ 消费组有一个或多个消费者,消费者可以是一个进程,也可以是一个线程
~~~ group.id是一个字符串,唯一标识一个消费组
~~~ 消费组订阅的主题每个分区只能分配给消费组一个消费者。
### --- 消费者位移(consumer position)
~~~ 消费者在消费的过程中记录已消费的数据,即消费位移(offset)信息。
~~~ 每个消费组保存自己的位移信息,那么只需要简单的一个整数表示位置就够了;
~~~ 同时可以引入checkpoint机制定期持久化。
二、位移管理(offset management)
### --- 自动VS手动
~~~ Kafka默认定期自动提交位移( enable.auto.commit = true ),也手动提交位移。
~~~ 另外kafka会定期把group消费情况保存起来,做成一个offset map,如下图所示:
### --- 位移提交
~~~ 位移是提交到Kafka中的__consumer_offsets 主题。
~~~ __consumer_offsets 中的消息保存了每个消费组某一时刻提交的offset信息。
[root@hadoop ~]# kafka-topics.sh --zookeeper localhost:2181/myKafka --list
__consumer_offsets
[root@hadoop ~]# kafka-console-consumer.sh --topic \
__consumer_offsets --bootstrap-server localhost:9092 \
--formatter "kafka.coordinator.group.GroupMetadataManager\$OffsetsMessageFormatter" \
--consumer.config /opt/yanqi/servers/kafka/config/consumer.properties \
--from-beginning | head
~~~ 输出参数
[test-consumer-group,__consumer_offsets,22]::[OffsetMetadata[108,NO_METADATA],CommitTime 1632243242054,ExpirationTime 1632329642054]
[test-consumer-group,__consumer_offsets,30]::[OffsetMetadata[0,NO_METADATA],CommitTime 1632243242054,ExpirationTime 1632329642054]
[test-consumer-group,__consumer_offsets,8]::[OffsetMetadata[0,NO_METADATA],CommitTime 1632243242054,ExpirationTime 1632329642054]
[test-consumer-group,__consumer_offsets,21]::[OffsetMetadata[8,NO_METADATA],CommitTime 1632243242054,ExpirationTime 1632329642054]
[test-consumer-group,__consumer_offsets,4]::[OffsetMetadata[515,NO_METADATA],CommitTime 1632243242054,ExpirationTime 1632329642054]
[test-consumer-group,__consumer_offsets,27]::[OffsetMetadata[0,NO_METADATA],CommitTime 1632243242054,ExpirationTime 1632329642054]
[test-consumer-group,__consumer_offsets,7]::[OffsetMetadata[0,NO_METADATA],CommitTime 1632243242054,ExpirationTime 1632329642054]
[test-consumer-group,__consumer_offsets,9]::[OffsetMetadata[0,NO_METADATA],CommitTime 1632243242054,ExpirationTime 1632329642054]
[test-consumer-group,__consumer_offsets,46]::[OffsetMetadata[2312,NO_METADATA],CommitTime 1632243242054,ExpirationTime 1632329642054]
[test-consumer-group,__consumer_offsets,25]::[OffsetMetadata[12,NO_METADATA],CommitTime 1632243242054,ExpirationTime 1632329642054]
~~~ # 上图中,标出来的,表示消费组为test-consumer-group ,
~~~ 消费的主题为__consumer_offsets ,消费的分区是4,偏移量为5。
~~~ __consumers_offsets 主题配置了compact策略,使得它总是能够保存最新的位移信息,
~~~ 既控制了该topic总体的日志容量,也能实现保存最新offset的目的。
三、再谈再均衡
### --- 什么是再均衡?
~~~ 再均衡(Rebalance)本质上是一种协议,
~~~ 规定了一个消费组中所有消费者如何达成一致来分配订阅主题的每个分区。
~~~ 比如某个消费组有20个消费组,订阅了一个具有100个分区的主题。
~~~ 正常情况下,Kafka平均会为每个消费者分配5个分区。这个分配的过程就叫再均衡。
### --- 什么时候再均衡?
~~~ # 再均衡的触发条件:
~~~ 组成员发生变更(新消费者加入消费组组、已有消费者主动离开或崩溃了)
~~~ 订阅主题数发生变更。如果正则表达式进行订阅,则新建匹配正则表达式的主题触发再均衡。
~~~ 订阅主题的分区数发生变更
### --- 如何进行组内分区分配?
~~~ 三种分配策略:RangeAssignor和RoundRobinAssignor以及StickyAssignor。后面讲。
### --- 谁来执行再均衡和消费组管理?
~~~ Kafka提供了一个角色:Group Coordinator来执行对于消费组的管理。
~~~ Group Coordinator——每个消费组分配一个消费组协调器用于组管理和位移管理。
~~~ 当消费组的第一个消费者启动的时候,它会去和Kafka Broker确定谁是它们组的组协调器。
~~~ 之后该消费组内所有消费者和该组协调器协调通信。
### --- 如何确定coordinator?
~~~ # 两步:
~~~ 确定消费组位移信息写入__consumers_offsets 的哪个分区。
~~~ # 具体计算公式:
~~~ __consumers_offsets partition# = Math.abs(groupId.hashCode() %
~~~ groupMetadataTopicPartitionCount) 注意:groupMetadataTopicPartitionCount
~~~ 由offsets.topic.num.partitions 指定,默认是50个分区。
~~~ 该分区leader所在的broker就是组协调器。
### --- Rebalance Generation
~~~ 它表示Rebalance之后主题分区到消费组中消费者映射关系的一个版本,
~~~ 主要是用于保护消费组,隔离无效偏移量提交的。
~~~ 如上一个版本的消费者无法提交位移到新版本的消费组中,
~~~ 因为映射关系变了,你消费的或许已经不是原来的那个分区了。
~~~ 每次group进行Rebalance之后,Generation号都会加1,
~~~ 表示消费组和分区的映射关系到了一个新版本,
### --- 如下图所示:
~~~ Generation 1时group有3个成员,随后成员2退出组,消费组协调器触发Rebalance,
~~~ 消费组进入Generation 2,之后成员4加入,
~~~ 再次触发Rebalance,消费组进入Generation 3.
### --- 协议(protocol)
~~~ # kafka提供了5个协议来处理与消费组协调相关的问题:
~~~ Heartbeat请求:consumer需要定期给组协调器发送心跳来表明自己还活着
~~~ LeaveGroup请求:主动告诉组协调器我要离开消费组
~~~ SyncGroup请求:消费组Leader把分配方案告诉组内所有成员
~~~ JoinGroup请求:成员请求加入组
~~~ DescribeGroup请求:显示组的所有信息,包括成员信息,协议名称,分配方案,订阅信息等。
~~~ 通常该请求是给管理员使用组协调器在再均衡的时候主要用到了前面4种请求。
### --- liveness
~~~ 消费者如何向消费组协调器证明自己还活着? 通过定时向消费组协调器发送Heartbeat请求。
~~~ 如果超过了设定的超时时间,那么协调器认为该消费者已经挂了。
~~~ 一旦协调器认为某个消费者挂了,那么它就会开启新一轮再均衡,
~~~ 并且在当前其他消费者的心跳响应中添加“REBALANCE_IN_PROGRESS”,
~~~ 告诉其他消费者:重新分配分区。
### --- 再均衡过程:再均衡分为2步:Join和Sync
~~~ Join, 加入组。
~~~ 所有成员都向消费组协调器发送JoinGroup请求,请求加入消费组。
~~~ 一旦所有成员都发送了JoinGroup请求,
~~~ 协调器从中选择一个消费者担任Leader的角色,并把组成员信息以及订阅信息发给Leader。
~~~ Sync,Leader开始分配消费方案,即哪个消费者负责消费哪些主题的哪些分区。
~~~ 一旦完成分配,Leader会将这个方案封装进SyncGroup请求中发给消费组协调器,
~~~ 非Leader也会发SyncGroup请求,只是内容为空。
~~~ 消费组协调器接收到分配方案之后会把方案塞进SyncGroup的response中发给各个消费者。
~~~ # 注意:
~~~ 在协调器收集到所有成员请求前,它会把已收到请求放入一个叫purgatory(炼狱)的地方。
~~~ 然后是分发分配方案的过程,即SyncGroup请求:
~~~ # 注意:
~~~ 消费组的分区分配方案在客户端执行。Kafka交给客户端可以有更好的灵活性。
~~~ Kafka默认提供三种分配策略:range和round-robin和sticky。
~~~ 可以通过消费者的参数:partition.assignment.strategy 来实现自己分配策略。
### --- 消费组状态机
~~~ 消费组组协调器根据状态机对消费组做不同的处理:
### --- 说明:
~~~ Dead:组内已经没有任何成员的最终状态,组的元数据也已经被组协调器移除了。
~~~ 这种状态响应各种请求都是一个response: UNKNOWN_MEMBER_ID
~~~ Empty:组内无成员,但是位移信息还没有过期。这种状态只能响应JoinGroup请求
~~~ PreparingRebalance:组准备开启新的rebalance,等待成员加入
~~~ AwaitingSync:正在等待leader consumer将分配方案传给各个成员
~~~ Stable:再均衡完成,可以开始消费。