kafka学习笔记(八) --- 消费者组到底是什么?

消费者组,即Consumer Group,kafka比较有亮点的设计之一。先用一句话概括就是:Consumer Group是Kafka提供的可扩展的且具有容错行的消费者机制。既然是一个组,那么组内,必然有多个消费者或者消费者实例,共享一个公共的ID,即Group ID。组内所有消费者协调在一起消费订阅主题所有分区,但是每个分区只能由同一个消费者组内的一个consumer实例消费。

Consumer Group具有以下三个特性:

  1. 可以有一个或多个Consumer实例,这个实例可以是单独一个进程,也可以是单独一个线程,实际场景中,使用进程更为常见。
  2. Group ID是一个字符串,在一个kafka集群中,标识唯一的一个Consumer Group。
  3. 被订阅的某个主题下的单个分区,只能分配给组内单个Consumer实例消费,当然也可以被其他组内的单个实例消费。

这里也提一下传统的消息引擎模型,点对点模型和发布/订阅模型,前者也称为消息队列。国内很多文章喜欢把消息中间件这类框架统称为消息队列,要区别于这里说的经典的消息引擎模型。那么,传统的消息引擎模型的两大特点:一是消息一旦被消费即被删除,而且只能被下游的一个consumer消费,这样伸缩性就很差,因为多个consumer要“抢”这个共享消息队列的消息;一是发布订阅模型虽然允许消息被多个Consumer消费,但是它的问题也是伸缩性不高,因为每个订阅者都必须订阅这个主题的所有分区,这种全量订阅方式既不灵活,也会影响消息真实投递效果。

Kafka的Consumer Group避开了上面这两种模型的缺陷,又兼具他们的优点。Consumer Group之间彼此独立,互不影响,能够订阅相同主题而互不干涉。再加上Broker端消息留存机制,Kafka的Consumer Group完美规避上面提到的伸缩性差的问题。可以这么说kafka仅仅使用Consumer Group这一种机制,同时实现了传统的消息引擎系统的两大模型:如果所有实例都属于同一个Group,那他就是消息队列模型;如果所有实例分别属于不同Group,那么就是发布/订阅模型。那么,在实际使用场景中,每个Group下该有多少个实例比较合适?理想情况下,Consumer实例的数量等于该Group订阅主题的分区总数,因为它能最大限度的实现高伸缩性。

接下来该说说Consumer怎么管理消费位移的呢?首先,什么是消费位移,它不同于分区位移,是记录消费者自己消费了多少数据,即消费位置信息,但是其实值的形式是一样的。Offset 是一组KV对,Key是分区,V对应Consumer消费该分区的最新位移,你可以简单的理解为Map<TopicPartition, long>,前者是消费分区,后者是位移类型。当然,Kafka源码中的才没有这样简单,相反要复杂得多,但是这样不妨碍我们理解。

老版本和新版本(0.9版本)的Consumer Group在使用上没有太多不一样,但是管理唯一的方式是不一样的。老版本会把位移保存在Zookeeper中。Apache Zookeeper是一个分布式的协调服务框架,Kafka重度依赖她实现各种各样的协调管理。将位移保存在Zookeeper中,最显而易见的好处就是减少了Broker端的状态保存开销,因为现在比较流行的提法是将服务器节点做成无状态的,这样可以自由扩容,实现超强伸缩性,Kafka最开始也是基于这样的考虑。

但是后来慢慢发现,Zookeeper这里元框架并不适合进行频繁的写更新,而Consumer Group的位移更新却是一个非常频繁的操作。大吞吐量的写操作势必会影响Zookeeper进群的性能,因此社区开始将Consumer位移保存在Kafka内部主题中,即__consumer_offsets。后面再详细介绍这个神秘的主题。

最后,来说说Consumer Group端的重平衡,即Rebalance过程。Rebalance本质上是一种协议,规定了一个Consumer Group下所有Consumer如何达成一致,来分配订阅Topic的每个分区。比如,某个Group下有20个Consumer实例,订阅了一个具有100个分区的Topic,正常情况下,kafka会为每个Consumer分配5个分区,这个分配过程就叫做Rebalance。

Rebalance的触发条件有3个:

  1. 组成员数发生变更。比如有新的实例加入或者离开组,亦或是有实例崩溃被“踢出”组。
  2. 订阅主题数发生变化。Consumer Group可以使用正则表达式的方式订阅主题,比如consumer.subscribe(Pattern.compile("t.*c")),表示Group订阅所有以字母 t 开头、字母 c 结尾的主题。在Group运行过程中,新创建了一个满足这样条件的主题,那么就会发生Rebalance。
  3. 订阅的主题分区数发生变更。kafka当前只允许增加主题的分区数。

Rebalance发生时,Group下所有Consumer实例都会协调一起共同参与,同时也需要分配策略的协助。当前Kafka默认提供3种分配策略,社区会不断完善分配策略,保证提供最公平的分配策略,即每个实例都能得到平均的分区数。举个简单例子,假设某Group下有两个Consumer,A和B,当第三个成员C加入时,kafka就会触发Rebalance,并根据默认分配策略重新为ABC分配分区。

再来说说Rebalance “遭人恨”的地方。首先,Rebalance过程对Group消费过程有极大影响。Rebalance过程中,所有Consumer实例都会停止消费,等待Rebalance完成,即著名的stop the world,简称STW。其次,目前Rebalance的设计是所有Consumer实力共同参与,全部重新参与分配所有分区。其实更高效的做法时尽量减少方案的变动。例如,实例A之前负责分区1、2、3,那么Rebalance之后,最好还是让它消费1、2、3,而不是分配其他分区,而且这样,A连接这些分区的TCP连接还可以继续用。最后,Rebalance是在太慢了。曾经,国外某个用户的Group内有几百个实例,成功Rebalance一次要几个小时,这是完全不能忍受的。所谓,“本事大不如不摊上”,也许最好的解决方案还是避免的Rebalance的发生吧。

标注:这个系列文章是本人在极客时间专栏---kafka核心技术与实战中的学习笔记

    https://time.geekbang.org/column/article/101171

发布了37 篇原创文章 · 获赞 20 · 访问量 4957

猜你喜欢

转载自blog.csdn.net/qq_24436765/article/details/102497176