Kafka核心技术与实战——15 | 消费者组到底是什么?

  • Consumer Group 是 Kafka 提供的可扩展且具有容错性的消费者机制
    • 既然是一个组,那么组内必然可以有多个消费者或消费者实例(Consumer Instance),它们共享一个公共的 ID,这个 ID 被称为 Group ID
    • 组内的所有消费者协调在一起来消费订阅主题(Subscribed Topics)的所有分区(Partition)
    • 当然,每个分区只能由同一个消费者组内的一个 Consumer 实例来消费
  • Consumer Group 记住下面这三个特性就好了。
    • 1、Consumer Group 下可以有一个或多个 Consumer 实例。这里的实例可以是一个单独的进程,也可以是同一进程下的线程。在实际场景中,使用进程更为常见一些。
    • 2、Group ID 是一个字符串,在一个 Kafka 集群中,它标识唯一的一个 Consumer Group。
    • 3、Consumer Group 下所有实例订阅的主题的单个分区,只能分配给组内的某个 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,那么通常情况下,为该 Group 设置 6 个 Consumer 实例是比较理想的情形,因为它能最大限度地实现高伸缩性
  • 针对 Consumer Group,Kafka 是怎么管理位移的呢?
    • 你还记得吧,消费者在消费的过程中需要记录自己消费了多少数据,即消费位置信息。在 Kafka 中,这个位置信息有个专门的术语:位移(Offset)。
    • 看上去该 Offset 就是一个数值而已,其实对于 Consumer Group 而言,它是一组 KV 对,Key 是分区,V 对应 Consumer 消费该分区的最新位移
    • 新版本的 Consumer Group 将位移保存在 Broker 端的内部主题中
  •  Consumer Group 端大名鼎鼎的重平衡,也就是所谓的 Rebalance 过程
    • Rebalance 本质上是一种协议,规定了一个 Consumer Group 下的所有 Consumer 如何达成一致,来分配订阅 Topic 的每个分区
    • 比如某个 Group 下有 20 个 Consumer 实例,它订阅了一个具有 100 个分区的 Topic
    • 正常情况下,Kafka 平均会为每个 Consumer 分配 5 个分区。这个分配的过程就叫 Rebalance
  • 那么 Consumer Group 何时进行 Rebalance 呢?Rebalance 的触发条件有 3 个。
    • 1、组成员数发生变更。比如有新的 Consumer 实例加入组或者离开组,抑或是有 Consumer 实例崩溃被“踢出”组。
    • 2、订阅主题数发生变更。Consumer Group 可以使用正则表达式的方式订阅主题,比如 consumer.subscribe(Pattern.compile(“t.*c”)) 就表明该 Group 订阅所有以字母 t 开头、字母 c 结尾的主题。在 Consumer Group 的运行过程中,你新创建了一个满足这样条件的主题,那么该 Group 就会发生 Rebalance。
    • 3、订阅主题的分区数发生变更。Kafka 当前只能允许增加一个主题的分区数。当分区数增加时,就会触发订阅该主题的所有 Group 开启 Rebalance。
  • 我们举个简单的例子来说明一下 Consumer Group 发生 Rebalance 的过程
    • 假设目前某个 Consumer Group 下有两个 Consumer,比如 A 和 B,当第三个成员 C 加入时,Kafka 会触发 Rebalance,并根据默认的分配策略重新为 A、B 和 C 分配分区,如下图所示
  • 讲完了 Rebalance,现在我来说说它“遭人恨”的地方
    • 首先,Rebalance 过程对 Consumer Group 消费过程有极大的影响。在 Rebalance 过程中,所有 Consumer 实例都会停止消费,等待 Rebalance 完成
    • 其次,目前 Rebalance 的设计是所有 Consumer 实例共同参与,全部重新分配所有分区。其实更高效的做法是尽量减少分配方案的变动。例如实例 A 之前负责消费分区 1、2、3,那么 Rebalance 之后,如果可能的话,最好还是让实例 A 继续消费分区 1、2、3,而不是被重新分配其他的分区。这样的话,实例 A 连接这些分区所在 Broker 的 TCP 连接就可以继续用,不用重新创建连接其他 Broker 的 Socket 资源。
    • 最后,Rebalance 实在是太慢了。曾经,有个国外用户的 Group 内有几百个 Consumer 实例,成功 Rebalance 一次要几个小时!这完全是不能忍受的
posted @ 2020-05-18 11:19  怡情养性长智  阅读(367)  评论(0编辑  收藏  举报