消费方式
消费者消费方式有pull
和push
两种。
push
指broker
推送数据给消费者,这样在固定的发送效率下处理能力差的消费者很容易出现网络拥堵。说白了就是一个劲的灌吃食,能吃就吃,吃不下噎着。
pull
指消费者主动拉取数据,这样的模式不足的地方在于如果kafka
中没有数据了,消费者就会陷入循环,一直返回空数据。就是消费者根据自己的能力来吃东西了,但是桌子上没东西了消费者也会夹空气吃。
针对这一点可以使用一个参数timeout
,当没有消息可以消费了,等待timeout
秒后消费者停止消费。
分区分配策略
一个consumer group
中有多个consumer
,一个 topic
有多个 partition
,所以必然会涉及到 partition
的分配问题,即确定那个partition
由哪个 consumer
来消费。
Kafka 有两种分配策略,一是 RoundRobin
,一是 Range
。
RoundRobin
RoundRobin的分配方式是轮询。
当只有一个生产者时:
多生产者
如果多生产者的话,就需要用到TopicAndPartition
类把所有的分区转换为对象,然后根据对象的hash值排序后做轮询。
Range
Range是默认的策略。
如果三个消费者都订阅了一个生产者:
如果多个消费者订阅了多个生产者:
在多生产者的情况下RoundRobin
轮询的方法确实比Range
直接分配的要均衡一些。但是RoundRobin
有一个极端情况:
就是一个消费者只订阅了一个主题,这样的话,这个主题里的数据就全部归这一个消费者了,间接造成了失衡,然后在0.11引入了一个新的分区策略:StickyAssignor
StickyAssignor
它主要有两个目的:
- 分区的分配要尽可能的均匀,分配给消费者者的主题分区数最多相差一个;
- 分区的分配尽可能的与上次分配的保持相同。
当两者发生冲突时,第一个目标优先于第二个目标。鉴于这两个目标,StickyAssignor
策略的具体实现要比RangeAssignor
和RoundRobinAssignor
这两种分配策略要复杂很多。
正常情况下这样分配
如果突然出现需要重新划分分区(有个消费者挂了),那么对他所拥有的分区如何划分呢,使用RoundRobin
会进行重新划分:
如果是StickyAssignor
则会在不影响现有分区的情况下进行划分:
原理简单,实现方便,结果优秀,这三者只能得其二。StickyAssignor
的代码实现方式比其他两种要复杂一些。
offset 的维护
由于 consumer 在消费过程中可能会出现断电宕机等故障,consumer
恢复后,需要从故障前的位置的继续消费,所以consumer
需要实时记录自己消费到了哪个offset
,以便故障恢复后继续消费。
Kafka 0.9 版本之前,consumer
默认将 offset
保存在 Zookeeper
中,从 0.9 版本开始,consumer
默认将 offset
保存在 Kafka 一个内置的 topic
中,该 topic
为__consumer_offsets
。
- 修改配置文件
consumer.properties
exclude.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