目录
- 消费者组 和 消费者实例
- ★ 消费者组
- ★ 分区 和 消费者实例
- ★ 分配分区(分区 和 消费者实例 之间的分配策略)
- 3种分配策略
- ★ range策略
- ★ round-robin策略 (轮询)
- ★ stick策略
- 如何指定分配分区的策略:
消费者组 和 消费者实例
★ 消费者组
形象来说:你可以把主题内的多个分区当成多个子任务、多个子任务组成项目,每个消费者实例就相当于一个员工,假如你们 team 包含2个员工。
项目 主题 team(2个员工)
5个子任务 分区
——每个子任务只会分给一个同一team的一个员工。
同理:
同一主题下,每个分区最多只会分给同一个组内的一个消费者实例
消费者以组的名义来订阅主题,前面的 kafka-console-consumer.bat 命令可通过–group选项来指定组ID。
一个主题包含多个分区,一个消费者组也包含多个消费者实例。同一个消费者组内的所有消费者共享一个公共的ID,这个ID被称为组ID。
同一组的多个消费者实例对外表现为一个整体(group、team),消费者实例整体一组的形式订阅一个主题。
同一消费组内的多个消费者实例一起协调消费主题所包含的全部分区,
因此:
同一个主题下每个分区只能由同一个组内的一个消费者实例来消费,但一个消费者实例可负责消费多个不同的分区。
由此可以发现:
同一消费者组内,多个消费者实例绝不可能获去同一个消息主题内的相同消息——这就是典型P2P模型。
不同消费者组,它们将会读到同一个消息主题内完全相同的消息,
——这就是典型Pub-Sub模型。
同一组内多个消费者实例就是模拟了P2P模型。不同组内多个消费者就是模拟了Pub-Sub模型。
简单来说:
1、一个主题有多个分区,一个消费者组有多个消费者实例
2、然后这个消费者组中的消费者实例A,它消费过的分区,其他同组的消费者实例B就不能去消费了,可以避免消息被重复消费。
3、但是一个消费者实例可以消费多个主题下的不同分区。
4、不同消费者组的消费者实例,可以读取同一个主题下的同一个分区里面的消息。
★ 分区 和 消费者实例
理想情况下,消费者组中的消费者实例的数量恰好等于该组所订阅主题内的总分区数,这样每个消费者实例恰好负责处理一个分区。
否则还有可能出现如下两种情况:
1、消费者实例数大于所订阅主题的分区数:
一个消费者实例要负责一个分区。但会有几个消费者实例处理空闲状态。
2、消费者实例数小于所订阅主题的分区数:
此时一个消费者实例要负责多个分区。
第一种情况无非是造成了消费者实例的浪费(消费者实例也是一个进程,但它永远无事可做);
但大部分时候,通常都是上面的第二种情况,此时就需要处理为消费者实例分配分区的问题。
★ 分配分区(分区 和 消费者实例 之间的分配策略)
3种分配策略
Kafka为消费者实例分配分区时提供了3种分配策略
就是为消费者实例分配它们要消费的分区的信息。
1、range策略。
2、round-robin策略。
3、sticky策略。
★ range策略
Kafka 默认是这个 range 策略
range策略看上去是公平,但实际上并不公平。
range策略是基于【每个主题】进行单独分配的,其大致步骤如下:
1、将每个主题内分区按数字顺序进行排序,消费者则按消费者名称的字典顺序进行排序。
2、用主题内的分区总数除以消费者总数,如果恰好能除尽,则每个消费者实例都分得相同数量(就是除得的商)的分区;
如果除不尽,排在前面的几个消费者将会分得额外的分区。
如图:一开始在想,为什么不是P0给C0,P1给C1,然后P2再给C0。
其实range策略是先算主题下有几个分区,然后消费者组有几个实例,然后计算每个实例消费几个分区,然后再来分配的。
如图,在range策略计算下,C0 要消费2个分区,C1要消费1个分区,所以在分配分区的时候,就把前面两个分区分配给C0实例,然后后面一个分区就分配给C1实例。
在有多个主题的情况下,range策略也是一个主题一个主题来单独分配的,
如图:现在有两个主题,一共有6个分区,然后有两个消费者实例,按理说应该一个实例消费三个分区,但是range策略是一个主题一个主题来分配的,
所以C0 在主题T0 分配到了两个分区,在主题T1那里也分配到了两个分区,
所以最后C0实例需要消费4个分区,而C1实例只需要消费2个分区,这个是不合理的。
这样容易造成 C1 实例的资源浪费
★ round-robin策略 (轮询)
round-robin策略会把【所有订阅主题的所有分区】按顺序排开,然后采用轮询方式依次分给各消费者实例。
由此可见,
round-robin策略 与 range策略 最大的不同就是它不再局限于单个主题进行分配,而是将所有订阅主题作为整体来进行分配
很明显,round-robin策略要更公平一些。
★ stick策略
——尽量只对改动部分进行重分配,尽力维护既有的分配方案。
sticky策略 主要是为了处理重平衡(rebalance) 需求,重平衡就是重新为消费者实例分配分区的过程。
比如以下三种情况就会触发重平衡:
-
组中消费者实例的数量发生变化。 比如有新的消费者实例加入消费者组,或者有消费者实例退出了消费者组。
-
订阅的主题数发生改变。 当消费者组以正则表达式的方式订阅主题时,符合正则表达式的主题可能会动态地变化。
-
某个或某几个订阅主题的分区数发生改变。当主题内分区数增加时,必须为之分配消费者实例来处理它。
当触发重平衡处理时,如果使用 range 或 round-robin策略,Kafka会【彻底抛弃】原有的分配方案,对变化后的消费者实例、分区进行彻底的重新分配。
而 sticky策略 则有效地避免了上述两种策略的缺点,sticky策略 会尽力维持了之前的分配方案,只对改动部分进行最小的再分配,因此通常认为 sticky策略 在处理重平衡时具有最佳的性能。
如何指定分配分区的策略:
可以在配置文件中指定策略,也可以在代码中指定。
通过如下属性:
partition.assignment.strategy
org.apache.kafka.clients.consumer.RangeAssignor:range策略
org.apache.kafka.clients.consumer.RoundRobinAssignor: round-robin策略
org.apache.kafka.clients.consumer.StickyAssignor: stick策略
百度查看是这么设置的,具体待后续研究: