15 | 消费者组到底是什么?
Posted 久违の欢喜
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了15 | 消费者组到底是什么?相关的知识,希望对你有一定的参考价值。
文章目录
Kafka 核心技术与实战
客户端实践及原理剖析
15 | 消费者组到底是什么?
Consumer Group
Consumer Group 是 Kafka 提供的可扩展且具有容错性的消费者机制。
- Consumer Group 下可以有一个或多个 Consumer 实例。这里的实例可以是一个单独的进程,也可以是同一进程下的线程。在实际场景中,使用进程更为常见一些。
- 多个消费者或消费者实例(Consumer Instance),它们共享一个公共的 ID,这个 ID 被称为 Group ID。Group ID 是一个字符串,在一个 Kafka 集群中,它标识唯一的一个 Consumer Group。
- 组内的所有消费者协调在一起来消费订阅主题(Subscribed Topics)的所有分区(Partition),每个分区只能由同一个消费者组内的一个 Consumer 实例来消费。这个分区当然也可以被其他的 Group 消费。
传统的点对点模型的缺陷在于消息一旦被消费,就会从队列中被删除,而且只能被下游的一个 Consumer 消费。这种模型的伸缩性(scalability)很差,因为下游的多个 Consumer 都要“抢”这个共享消息队列的消息。
发布 / 订阅模型允许消息被多个 Consumer 消费,但它的问题也是伸缩性不高,因为每个订阅者都必须要订阅主题的所有分区。这种全量订阅的方式既不灵活,也会影响消息的真实投递效果。
Kafka 的 Consumer Group 的机制既可以避开这两种模型的缺陷,又兼具它们的优点。当 Consumer Group 订阅了多个主题后,组内的每个实例不要求一定要订阅主题的所有分区,它只会消费部分分区中的消息。
Consumer Group 之间彼此独立,互不影响,它们能够订阅相同的一组主题而互不干涉。Broker 端的消息留存机制,Kafka 的 Consumer Group 完美地规避了上面提到的伸缩性差的问题。可以这么说,Kafka 仅仅使用 Consumer Group 这一种机制,却同时实现了传统消息引擎系统的两大模型:如果所有实例都属于同一个 Group,那么它实现的就是点对点模型;如果所有实例分别属于不同的 Group,那么它实现的就是发布 / 订阅模型。
在实际使用场景中,一个 Group 下该有多少个 Consumer 实例呢?
理想情况下,Consumer 实例的数量应该等于该 Group 订阅主题的分区总数。
假设一个 Consumer Group 订阅了 3 个主题,分别是 A、B、C,它们的分区数依次是 1、2、3(总共是 6 个分区),那么通常情况下,为该 Group 设置 6 个 Consumer 实例是比较理想的情形,因为它能最大限度地实现高伸缩性。
位移(Offset)
针对 Consumer Group,Kafka 是怎么管理位移(Offset)的呢?
对于 Consumer Group 而言,它是一组 KV 对,Key 是分区,V 对应 Consumer 消费该分区的最新位移。如果用 Java 来表示可以理解为 Map<TopicPartition, Long> 这样一个数据结构,其中 TopicPartition 表示一个分区,而 Long 表示位移的类型。
Kafka 有新旧客户端 API 之分,那自然也就有新旧 Consumer 之分。老版本的 Consumer 也有消费者组的概念和 Consumer Group 在使用感上并没有太多的不同,只是它管理位移的方式和新版本是不一样的。
老版本的 Consumer Group 把位移保存在 ZooKeeper 中。Apache ZooKeeper 是一个分布式的协调服务框架,Kafka 重度依赖它实现各种各样的协调管理。将位移保存在 ZooKeeper 外部系统的做法,最显而易见的好处就是减少了 Kafka Broker 端的状态保存开销。现在比较流行的提法是将服务器节点做成无状态的,这样可以自由地扩缩容,实现超强的伸缩性。Kafka 最开始也是基于这样的考虑,才将 Consumer Group 位移保存在独立于 Kafka 集群之外的框架中。
ZooKeeper 这类元框架其实并不适合进行频繁的写更新,而 Consumer Group 的位移更新却是一个非常频繁的操作。这种大吞吐量的写操作会极大地拖慢 ZooKeeper 集群的性能,因此 Kafka 社区渐渐有了这样的共识:将 Consumer 位移保存在 ZooKeeper 中是不合适的做法。
在新版本的 Consumer Group 中,Kafka 社区重新设计了 Consumer Group 的位移管理方式,采用了将位移保存在 Kafka 内部主题的方法,即 __consumer_offsets。
Rebalance
Rebalance 本质上是一种协议,规定了一个 Consumer Group 下的所有 Consumer 如何达成一致,来分配订阅 Topic 的每个分区。 比如某个 Group 下有 20 个 Consumer 实例,它订阅了一个具有 100 个分区的 Topic。正常情况下,Kafka 平均会为每个 Consumer 分配 5 个分区,这个分配的过程就叫 Rebalance。
Rebalance 的触发条件有 3 个:
- 组成员数发生变更。 比如有新的 Consumer 实例加入组或者离开组,抑或是有 Consumer 实例崩溃被“踢出”组。
- 订阅主题数发生变更。 Consumer Group 可以使用正则表达式的方式订阅主题,比如 consumer.subscribe(Pattern.compile(“t.*c”)) 就表明该 Group 订阅所有以字母 t 开头、字母 c 结尾的主题。在 Consumer Group 的运行过程中,新建一个满足这样条件的主题,那么该 Group 就会发生 Rebalance。
- 订阅主题的分区数发生变更。 Kafka 当前只能允许增加一个主题的分区数。当分区数增加时,就会触发订阅该主题的所有 Group 开启 Rebalance。
Rebalance 发生时,Group 下所有的 Consumer 实例都会协调在一起共同参与。
假设目前某个 Consumer Group 下有两个 Consumer,比如 A 和 B,当第三个成员 C 加入时,Kafka 会触发 Rebalance,并根据默认的分配策略重新为 A、B 和 C 分配分区,如下图所示:
Rebalance 让人诟病的地方:
- 在 Rebalance 过程中,所有 Consumer 实例都会停止消费,等待 Rebalance 完成。
- 目前 Rebalance 的设计是所有 Consumer 实例共同参与,全部重新分配所有分区。 其实更高效的做法是尽量减少分配方案的变动。
- Rebalance 太耗时。
以上是关于15 | 消费者组到底是什么?的主要内容,如果未能解决你的问题,请参考以下文章