消费方式

消费者消费方式有pullpush两种。

pushbroker推送数据给消费者,这样在固定的发送效率下处理能力差的消费者很容易出现网络拥堵。说白了就是一个劲的灌吃食,能吃就吃,吃不下噎着。

pull指消费者主动拉取数据,这样的模式不足的地方在于如果kafka中没有数据了,消费者就会陷入循环,一直返回空数据。就是消费者根据自己的能力来吃东西了,但是桌子上没东西了消费者也会夹空气吃。

针对这一点可以使用一个参数timeout,当没有消息可以消费了,等待timeout秒后消费者停止消费。

分区分配策略

一个consumer group中有多个consumer,一个 topic有多个 partition,所以必然会涉及到 partition的分配问题,即确定那个partition由哪个 consumer来消费。
Kafka 有两种分配策略,一是 RoundRobin,一是 Range

RoundRobin

RoundRobin的分配方式是轮询。

当只有一个生产者时:

kafka 消费服务 kafka消费模式有哪几种_数据

多生产者

kafka 消费服务 kafka消费模式有哪几种_数据_02


如果多生产者的话,就需要用到TopicAndPartition类把所有的分区转换为对象,然后根据对象的hash值排序后做轮询。

Range

Range是默认的策略。

如果三个消费者都订阅了一个生产者:

kafka 消费服务 kafka消费模式有哪几种_zookeeper_03


如果多个消费者订阅了多个生产者:

kafka 消费服务 kafka消费模式有哪几种_数据_04


在多生产者的情况下RoundRobin轮询的方法确实比Range直接分配的要均衡一些。但是RoundRobin有一个极端情况:

就是一个消费者只订阅了一个主题,这样的话,这个主题里的数据就全部归这一个消费者了,间接造成了失衡,然后在0.11引入了一个新的分区策略:StickyAssignor

StickyAssignor

它主要有两个目的:

  • 分区的分配要尽可能的均匀,分配给消费者者的主题分区数最多相差一个;
  • 分区的分配尽可能的与上次分配的保持相同。

当两者发生冲突时,第一个目标优先于第二个目标。鉴于这两个目标,StickyAssignor策略的具体实现要比RangeAssignorRoundRobinAssignor这两种分配策略要复杂很多。

正常情况下这样分配

kafka 消费服务 kafka消费模式有哪几种_数据_05

如果突然出现需要重新划分分区(有个消费者挂了),那么对他所拥有的分区如何划分呢,使用RoundRobin会进行重新划分:

kafka 消费服务 kafka消费模式有哪几种_zookeeper_06


如果是StickyAssignor则会在不影响现有分区的情况下进行划分:

kafka 消费服务 kafka消费模式有哪几种_kafka 消费服务_07

原理简单,实现方便,结果优秀,这三者只能得其二。StickyAssignor的代码实现方式比其他两种要复杂一些。

offset 的维护

由于 consumer 在消费过程中可能会出现断电宕机等故障,consumer 恢复后,需要从故障前的位置的继续消费,所以consumer需要实时记录自己消费到了哪个offset,以便故障恢复后继续消费。

Kafka 0.9 版本之前,consumer默认将 offset保存在 Zookeeper中,从 0.9 版本开始,consumer默认将 offset保存在 Kafka 一个内置的 topic 中,该 topic__consumer_offsets

  • 修改配置文件 consumer.propertiesexclude.internal.topics=false
  • 读取 offset

0.11.0.0 之前版本:

bin/kafka-console-consumer.sh \
--topic __consumer_offsets \
--zookeeper hadoop102:2181 \
--formatter "kafka.coordinator.GroupMetadataManager\$OffsetsMessageFormatter" \
--consumer.config config/consumer.properties \ 
--from-beginning

0.11.0.0 之后版本(含):

bin/kafka-console-consumer.sh \
--topic __consumer_offsets \
--zookeeper hadoop102:2181 \
--formatter "kafka.coordinator.group.GroupMetadataManager\$OffsetsMessageFormatter" \
--consumer.config config/consumer.properties \
--from-beginning