Kafka05——消费者

一、消费方式
consumer 采用 pull(拉)模式从 broker 中读取数据。
 push(推)模式很难适应消费速率不同的消费者,因为消息发送速率是由 broker 决定的。它的目标是尽可能以最快速度传递消息,但是这样很容易造成 consumer 来不及处理消息,典型的表现就是拒绝服务以及网络拥塞。而 pull 模式则可以根据 consumer 的消费能力以适当的速率消费消息。
 pull 模式不足之处是,如果 kafka 没有数据,消费者可能会陷入循环中,一直返回空数据。针对这一点,Kafka 的消费者在消费数据时会传入一个时长参数 timeout,如果当前没有数据可供消费,consumer 会等待一段时间之后再返回,这段时长即为 timeout。

二、分区分配策略
 一个 consumer group 中有多个 consumer,一个 topic 有多个 partition,所以必然会涉及到 partition 的分配问题,即确定哪个 partition 由哪个 consumer 来消费,以保证topic中的同一条消息只能被消费者组中的其中一个(有且仅有一个消费者)消费者消费
 Kafka 有三种分配策略:一是 Range(默认的策略),二是 RoundRobin,三是 Sticky;详参:Kafka中消费者的分区分配策略
 Kafka提供了消费者客户端参数partition.assignment.strategy用来设置消费者与订阅主题之间的分区分配策略。默认情况下,此参数的值为:org.apache.kafka.clients.consumer.RangeAssignor,即采用RangeAssignor分配策略。
 1、RangeAssignor分配策略
  RangeAssignor 策略的原理是按照消费者总数和分区总数进行整除运算来获得一个跨度,然后将分区按照跨度进行平均分配,以保证分区尽可能均匀地分配给所有的消费者。对于每一个topic,RangeAssignor 策略会将消费组内所有订阅这个topic的消费者按照名称的字典序排序,然后为每个消费者划分固定的分区范围,如果不够平均分配,那么字典序靠前的消费者会被多分配一个分区。
  假设消费组内有2个消费者C0和C1,都订阅了主题t0和t1,并且每个主题都有4个分区,那么所订阅的所有分区可以标识为:t0p0、t0p1、t0p2、t0p3、t1p0、t1p1、t1p2、t1p3。最终的分配结果为:
在这里插入图片描述
  我们再来看下另外一种情况。假设上面例子中2个主题都只有3个分区,那么所订阅的所有分区可以标识为:t0p0、t0p1、t0p2、t1p0、t1p1、t1p2。最终的分配结果为:
在这里插入图片描述
  可以明显的看到这样的分配并不均匀,如果将类似的情形扩大,有可能会出现部分消费者过载的情况。

 2、RoundRobinAssignor分配策略
  RoundRobinAssignor 策略的原理是将消费组内所有消费者以及消费者所订阅的所有topic的 partition 按照字典序排序,然后通过轮询消费者方式逐个将分区分配给每个消费者。RoundRobinAssignor 策略对应的 partition.assignment.strategy 参数值为:org.apache.kafka.clients.consumer.RoundRobinAssignor。
  如果同一个消费组内所有的消费者的订阅信息都是相同的(订阅的topic都相同),那么RoundRobinAssignor 策略的分区分配会是均匀的。假设消费组中有2个消费者C0和C1,都订阅了主题t0和t1,并且每个主题都有3个分区,那么所订阅的所有分区可以标识为:t0p0、t0p1、t0p2、t1p0、t1p1、t1p2。最终的分配结果为:
在这里插入图片描述
  如果同一个消费组内的消费者所订阅的Topic 是不相同的,那么在执行分区分配的时候就不是完全的轮询分配,有可能会导致分区分配的不均匀。如果某个消费者没有订阅消费组内的某个topic,那么在分配分区的时候此消费者将分配不到这个topic的任何分区。假设消费组内有3个消费者C0、C1和C2,它们共订阅了3个主题:t0、t1、t2,这3个主题分别有1、2、3个分区,即整个消费组订阅了t0p0、t1p0、t1p1、t2p0、t2p1、t2p2这6个分区。如果消费者C0订阅的是主题t0,消费者C1订阅的是主题t0和t1,消费者C2订阅的是主题t0、t1和t2,那么最终的分配结果为:红线表示消费者订阅的topic
在这里插入图片描述
  可以看到 RoundRobinAssignor 策略也不是十分完美,这样分配其实并不是最优解,因为完全可以将分区t1p1分配给消费者C1,如下图:
在这里插入图片描述
 3、StickyAssignor分配策略
  StickyAssignor 策略,“sticky”这个单词可以翻译为“粘性的”,Kafka从0.11.x版本开始引入这种分配策略,它主要有两个目的:
   ①分区的分配要尽可能的均匀;
   ②分区的分配尽可能的与上次分配的保持相同;
  当两者发生冲突时,第一个目标优先于第二个目标。鉴于这两个目标,StickyAssignor 策略的具体实现要比 RangeAssignor 和 RoundRobinAssignor 这两种分配策略要复杂很多。我们举例来看一下StickyAssignor策略的实际效果。
  假设消费组内有3个消费者:C0、C1和C2,它们都订阅了4个主题:t0、t1、t2、t3,并且每个主题有2个分区,也就是说整个消费组订阅了t0p0、t0p1、t1p0、t1p1、t2p0、t2p1、t3p0、t3p1这8个分区。最终的分配结果如下:
在这里插入图片描述
  这样初看上去似乎与采用 RoundRobinAssignor 策略所分配的结果相同,但事实是否真的如此呢?再假设此时消费者C1脱离了消费组,那么消费组就会执行再平衡操作,进而消费分区会重新分配。如果采用 RoundRobinAssignor 策略,那么此时的分配结果如下:
在这里插入图片描述
  RoundRobinAssignor 策略会按照消费者C0和C2进行重新轮询分配。而如果此时使用的是 StickyAssignor 策略,那么分配结果为:
在这里插入图片描述
  可以看到分配结果中保留了上一次分配中对于消费者C0和C2的所有分配结果,并将原来消费者C1的“负担”分配给了剩余的两个消费者C0和C2,最终C0和C2的分配还保持了均衡。
  如果发生分区重分配,那么对于同一个分区而言有可能之前的消费者和新指派的消费者不是同一个,对于之前消费者进行到一半的处理还要在新指派的消费者中再次复现一遍,这显然很浪费系统资源。StickyAssignor策略如同其名称中的“sticky”一样,让分配策略具备一定的“粘性”,尽可能地让前后两次分配相同,进而减少系统资源的损耗以及其它异常情况的发生。
  同样消费组内有3个消费者:C0、C1和C2,集群中有3个主题:t0、t1和t2,这3个主题分别有1、2、3个分区,也就是说集群中有t0p0、t1p0、t1p1、t2p0、t2p1、t2p2这6个分区。消费者C0订阅了主题t0,消费者C1订阅了主题t0和t1,消费者C2订阅了主题t0、t1和t2。如果此时采用的是StickyAssignor策略,那么最终的分配结果为:
在这里插入图片描述
  可以看到这是一个最优解:消费者C0没有订阅主题t1和t2,所以不能分配主题t1和t2中的任何分区给它,对于消费者C1也是同理。
  StickyAssignor 策略消费者脱离消费组的情况:
在这里插入图片描述
  只是将脱离的消费者的分区进行再分配。
  从结果上看StickyAssignor策略相较于另外两个分配策略而言显得更加的优异,这个策略的代码实现也是异常复杂,如果大家在一个 group 里面,不同的 Consumer 订阅不同的 topic, 那么设置Sticky 分配策略还是很有必要的。

三、offset 的维护
 由于 consumer 在消费过程中可能会出现断电宕机等故障,consumer 恢复后,需要从故障前的位置继续消费,所以 consumer 需要实时记录自己消费到了哪个 offset,以便故障恢复后继续消费。
在这里插入图片描述
 Kafka 0.9 版本之前,consumer 默认将 offset 保存在 Zookeeper 中。从 0.9 版本开始,consumer 默认将 offset 保存在 Kafka 一个内置的 topic 中,该 topic 名为__consumer_offsets,这个topic被划分为了很多个分区(默认50个),每个分区中都记录了各个消费者组消费的各个topic的消息的offset。这个记录是增量的,即一旦有消费者消费了topic中的消息,就会向__consumer_offsets的某个分区添加一条消息,该消息中记录了该消费者所属的消费者组、消费的消息的topic、消息的offset、时间等信息。
 我们可以通过修改 consumer.properties配置文件中的参数,依然让offset的信息保存在zookeeper中:

exclude.internal.topics=false

 通过命令获取消费者组消费的offset:
  0.11.0.0 之前版本:

./kafka-console-consumer.sh --topic __consumer_offsets --bootstrap-server 192.168.56.93:9092 --formatter "kafka.coordinator.GroupMetadataManager\$OffsetsMessageFormatter" --consumer.config ../config/consumer.properties --from-beginning

  0.11.0.0 之后版本(含0.11.0.0):只是执行 formatter 的类放在了不同的包下

./kafka-console-consumer.sh --topic __consumer_offsets --bootstrap-server 192.168.56.93:9092 --formatter "kafka.coordinator.group.GroupMetadataManager\$OffsetsMessageFormatter" --consumer.config ../config/consumer.properties --from-beginning
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值