kafka消息消费原理

在实际生产过程中,每个topic都会有多个partitions,多个partitions的好处在于,一方面能够对broker上的数据进行分片有效减少了消息的容量从而提升io性能。另外一方面,为了提高消费端的消费能力,一般会通过多个consumer去消费同一个topic ,也就是消费端的负载均衡机制,也就是我们接下来要了解的,在多个partition以及多个consumer的情况下,消费者是如何消费消息的

分区内消息有序

每个消息在被添加到分区时,都会被分配一个offset(偏移量),它是消息在此分区中的唯一编号,kafka通过offset保证消息在分区内的顺序,offset的顺序不跨分区,即kafka只保证在同一个分区内的消息是有序的

每一条消息发送到broker时,会根据partition的规则选择存储到哪一个partition。如果partition规则设置合理,那么所有的消息会均匀的分布在不同的partition中,这样就有点类似数据库的分库分表的概念,把数据做了分片处理
下图中,对于名字为test的topic,做了3个分区,分别是p0、p1、p2:

kafka消息如何分片的 kafka消息分配策略_kafka

kafka消息分发策略

消息默认的分发机制
默认情况下,kafka采用的是hash取模的分区算法。如果key为null,则会随机分配一个分区

对于上面这个图来说,这3个消费者会分别消费test这个topic 的3个分区,也就是每个consumer消费一个partition

消费者和分区的数量建议

  1. 如果consumer比partition多,是浪费,因为kafka的设计是在一个partition上是不允许并发的,所以consumer数不要大于partition数
  2. 如果consumer比partition少,一个消费者会对应于多个分区,这里要合理分配消费者数和分区数,否则会导致分区里面的数据被取的不均匀。最好分区数目是消费者数目的整数倍,所以partition数目很重要,比如取24,就很容易设定消费者数目

分区分配操作
增减consumer,broker,partition都会导致rebalance(分区分配),rebalance后consumer对应的partition会发生变化。kafka consuemr的分区分配机制规定了一个consumer group下的所有consumer如何达成一致来分配订阅topic的每个分区。而具体如何执行依靠分区策略

什么时候会触发分区分配呢?
当出现以下几种情况时,kafka会进行一次分区分配操作

  1. 同一个consumer group内新增了消费者
  2. 消费者离开当前所属的consumer group,比如主动停机或者宕机
  3. topic新增了分区(也就是分区数量发生了变化)

在kafka中,存在三种分区分配策略,分别是:

  • Range(范围分区)(默认)
  • RoundRobin(轮询)
  • StickyAssignor(粘性)

谁来执行Rebalance以及管理consumer的group呢?
Kafka提供了一个角色:coordinator来执行对于consumer group的管理,Kafka提供了一个角色:coordinator来执行对于consumer group的管理,当consumer group的第一个consumer启动的时候,它会去和kafka server确定谁是它们组的coordinator。之后该group内的所有成员都会和该coordinator进行协调通信

如何确定coordinator
消费者向kafka集群中的任意一个broker发送一个GroupCoordinatorRequest请求,服务端会返回一个负载最小的broker节点的id,并将该broker设置为coordinator

JoinGroup的过程
在rebalance之前,需要保证coordinator是已经确定好了的,整个rebalance的过程分为两个步骤,Join和Sync

join: 表示加入到consumer group中,在这一步中,所有的成员都会向coordinator发送joinGroup的请求。一旦所有成员都发送了joinGroup请求,那么coordinator会选择一个consumer担任leader角色,并把组成员信息和订阅信息发送消费者

coordinator选择leader的算法比较简单,如果消费组内没有leader,那么第一个加入消费组的消费者就是消费者leader,如果这个时候leader消费者退出了消费组,那么重新选举一个leader,这个选举很随意,类似于随机算法

  • protocol_metadata: 序列化后的消费者的订阅信息
  • leader_id: 消费组中的消费者,coordinator会选择一个座位leader,对应的就是member_id
  • member_metadata 对应消费者的订阅信息
  • members:consumer group中全部的消费者的订阅信息
  • generation_id: 年代信息,类似于zookeeper的epoch,对于每一轮rebalance,generation_id都会递增。主要用来保护consumer group。隔离无效的offset提交。也就是上一轮的consumer成员无法提交offset到新consumer group中

消费组的分区分配策略
每个消费者都可以设置自己的分区分配策略,对于消费组而言,会从各个消费者上报过来的分区分配策略中选举一个彼此都赞同的策略来实现整体的分区分配,这个"赞同"的规则是,消费组内的各个消费者会通过投票来决定

  • 在joingroup阶段,每个consumer都会把自己支持的分区分配策略发送到coordinator
  • coordinator手机到所有消费者的分配策略,组成一个候选集
  • 每个消费者需要从候选集里找出一个自己支持的策略,并且为这个策略投票
  • 最终计算候选集中各个策略的选票数,票数最多的就是当前消费组的分配策略

Synchronizing Group State阶段
分区分配完成之后,就进入了Synchronizing Group State阶段,主要逻辑是向GroupCoordinator发送SyncGroupRequest请求,并且处理SyncGroupResponse响应,简单来说,就是leader将消费者对应的partition分配方案同步给consumer group 中的所有consumer

kafka消息如何分片的 kafka消息分配策略_Group_02


每个消费者都会向coordinator发送syncgroup请求,不过只有leader节点会发送分配方案,其他消费者只是打打酱油而已。当leader把方案发给coordinator以后,coordinator会把结果设置到SyncGroupResponse中。这样所有成员都知道自己应该消费哪个分区

消息的发送分区策略过程总结

  1. 对于每个consumer group,kafka都会在服务端对应一个GroupCoordinator进行管理,GroupCoordinator会在zookeeper上添加watcher,当消费者加入或者退出consumer group时,会修改zookeeper上保存的数据,从而触发GroupCoordinator开始Rebalance操作
  2. 当消费者准备加入某个Consumer group,消费者并不知道GroupCoordinator在网络中的位置,消费者会向集群中的任意一个Broker节点发送ConsumerMetadataRequest请求,收到请求的broker会返回一个response作为响应,其中包含管理当前ConsumerGroup的GroupCoordinator
  3. 消费者会根据broker的返回信息,连接到GroupCoordinator,并且发送HeartbeatRequest,发送心跳的目的是要让GroupCoordinator知道这个消费者是正常在线的。当消费者在指定时间内没有发送心跳请求,则GroupCoordinator会触发Rebalance操作
  4. consumer确定好了GroupCoordinator以后,会向GroupCoordinator发起join group请求,GroupCoordinator会收集全部消费者信息之后,来确认可用的消费者,并从中选取一个消费者成为group_leader。并把相应的信息(分区分配策略、leader_id、…)封装成response返回给所有消费者,但是只有group leader会收到当前consumer group中的所有消费者信息
  5. 当某一个consumer确定自己是group leader以后,会根据所在分组的consumer的信息选定分区分配策略进行分区分配
  6. 接着进入Synchronizing Group State阶段,每个消费者会发送SyncGroupRequest请求到GroupCoordinator,但是只有Group Leader的请求会存在分区分配结果,GroupCoordinator会根据Group Leader的分区分配结果形成SyncGroupResponse 返回给所有的Consumer
  7. consumer根据分配结果,执行相应的操作

到这里为止,我们了解到了整个的消息的发送分区策略,以及消费者的分区消费策略和rebalance。,还有一个重要的东西offset,他类似一个游标,表示当前消费的消息的位置

如何保存消费端的消费位置

什么是offset?
在partition的时候我们提到过offset, 每个topic可以划分多个分区(每个Topic至少有一个分区),同一topic下的不同分区包含的消息是不同的。每个消息在被添加到分区时,都会被分配一个offset(称之为偏移量),它是消息在此分区中的唯一编号,kafka通过offset保证消息在分区内的顺序,offset的顺序不跨分区,即kafka只保证在同一个分区内的消息是有序的; 对于应用层的消费来说,每次消费一个消息并且提交以后,会保存当前消费到的最近的一个offset

kafka消息如何分片的 kafka消息分配策略_kafka消息如何分片的_03

offset在哪里维护?
在kafka中,提供了一个consumer_offsets_* 的一个topic,consumer_offsets保存了每个consumer group某一时刻提交的offset信息
__consumer_offsets 默认有50个分区

执行如下命令,可以查看当前consumer_goup中的offset位移提交的信息

kafka-console-consumer.sh --topic __consumer_offsets --partition 15 -- bootstrap-server 192.168.13.102:9092,192.168.13.103:9092,192.168.13.104:9092 --formatter 'kafka.coordinator.group.GroupMetadataManager$OffsetsMessageFormatter'