深入理解kafka核心设计与实践原理-消费者

2023-07-28  本文已影响0人  叫我小码哥
image.png
前面已经讲述了生产者 https://www.jianshu.com/p/40afc57471da,这章节我们看下消费者。

消费者,消费者组的关系

消费者(Consumer)负责订阅Kafka 中的主题(Topic), 并且从订阅的主题上拉取消息。
与其他一些消息中间件不同的是:在kafka 的消费理念中还有一层消费组(Consumer Group)
的概念,每个消费者都有一个对应的消费组。 当消息发布到主题后, 只会被投递给订阅它的每个消费组中的一个消费者。

消费者,消费者组,主题,分区的关系

消费者,消费者组,主题,分区之间有什么关系小编用小面一个例子进行说明。

如图1所示,该主题下有2个分区分别为group A和group B,group A中有4个消费者,group B中有2个消费者,按照kakfa的默认规则, group A和group B消费者组中的消费者只能分配到一个分区,但消费者组之间是相互不影响的。


图1

我们再看一下图2的情况,消费组内的消费者个数变化时所对应的分区分配的演变。 假设目前某消费
组内只有一个消费者C0, 订阅了一个主题, 这个主题包含7个分区: P0、 P1、 P2、 P3、 P4、P5、 P6。 也就是说, 这个消费者C0, 订阅了7个分区。

此时消费者组增加新的消费C1如图3所示,按照kafka规则需要将原来C0的消费的部分分区让给C1消费 (消费者组变动,分区动态调整原理后面会讲到)

图2
图3

消费者与消费组这种模型可以让整体的消费能力具备横向伸缩性,我们可以增加(或减少)消费者的个数来提高(或降低)整体的消费能力。一味地增加消费者并不会让消费能力一直得到提升,如果消费者过多,出现了消费者的个数大于分区个数的清况,就会有消费者分配不到任何分区。参考图5, 消费者8个,分区7个,那么嘴一分配的消费C7分配不到任何分区而无法消费任何消息。


图4
图5

kakfa消息的投递模式

对于消息队列而言,常见的消息投递模式有2种,第一种是点对点的模式我们称之为p2p,另一种是发布订阅模式我们称之为(Pub/Sub)
点对点模式是基于队列的,消息生产者发送消息到队列,消息消费者从队列中接收消息。
发布订阅模式定义了如何向一个内容节点发布和订阅消息,这个内容节点称为主题(Topic) , 主题可以认为是消息传递的渠道,消息发布者将消息发布到某个主题,而消息订阅者从主题中订阅消息。主题使得消息的订阅者和发布者互相保持独立,不需要进行。
Kaflca同时支待两种消息投递模式:
第一种是消费者都隶属于同一个消费组,那么所有的消息都会被均衡地投递给每一个消费者,即每条消息只会被一个消费者处理,这就相当千点对点模式的应用。
第二种消费者都隶属于不同的消费组,那么所有的消息都会被广播给所有的消费
者,即每条消息会被所有的消费者处理,这就相当于发布/订阅模式的应用。
消费组是一个逻辑上的概念,主要是将消费者进行分类,每一个消费者只隶属于一个消费组。每一个消费组都会有一个固定的名称,消费者在进行消费前需要指定其所属消费组的名称。同一个消费组内的消费者既可以部署在同一台机器上,也可以部署在不同的机器上。

消费模式

消息的消费一般有两种模式:推模式和 拉模式。
推模式是服务端主动将消息推送给消费者, 而拉模式是消费者主动向服务端发起请求来拉取消息。

消费者位移提交

对于 Kafka 中的分区而言,它的每条消息都有唯 offset ,用来表示消息在分区中对应位置。对于消费者, 它也有 offset 的概念,消费者使用 offset 来表示消费到分区中某个消息所在的位置。对于 offset的说明,对于消息在分区中的位置 offset
称为“偏移量” 对于消费者消费到的位置,将 offset 称为“位移“ 有时候也会更明确地称之为“消费位移”
在每次调用 poll() 方法时,它返回的是还没有被消费过的消息集(当然这个前提是消息己经存储在 Kafka 了,并且暂不考虑异常情况的发生) 要做到这一点,就需要录上次消费时的消费位移 并且这个消费位移必须做持久化保存,而不是单单保存在内存中,否则消费者重启之后就无法知晓之前的消费位移 再考虑一种情况,当有新的消费者加入时,那么必然会有再均衡的动作 对于相同分区而言,它可能在再均衡动作之后分配给新的消费者。如果不持久化保存消费位移,那么这个新的消费者也无法知晓之前的消费位移在旧消费者客户端中,消费位移是存储在 ZooKeeper 中的 而在新消费者客户端中,消费位移存储在 Kafka 部的主题 consumer offsets 这里把将消费位移存储起来(持久化)的动作称为“提交’ ,消费者在消费完消息之后需要执行消费位移的提交。

如图6所示的消费位移X表示某一次拉取操作中此分区消息最大偏移量 ,假设当前消费者已经消费了X位置的消息,那么我们就可以说消费者的消 费位移 ,图中也用了lastConsumedOffset 这个单词来标识它。


图6

不过需要非常明确的 当前消费者需要提交的消 费位移并不是 ,而是 ,对应于图
中的 position ,它表示 一条需要拉取的消息的位置。读者可能看过一些相关资料,里面所
讲述的内容可能是提交的消费位移就是当前所消费到的消费位移, 即提 的是 ,这明显是错
误的。类似的错误还体现在对 LEO ( Log End Offset 解读上,与此相关的细节可以参阅第
章的内容。在消费者中还有一个 committed offset 的概念,它表示已经提交过的消费位移。

不过需要非常明确的是,当前消费者需要提交的消费费位移并不是X ,而是 X+1,对应于图6中的 position ,它表示下一条需要拉取的消息的位置。

消息丢失,重复消费问题

对于位移提交的具体时机的把握也很有讲究,有可能会造成重复消费和消息丢失的现象。

如图 7 ,当前 poll()操作所拉取的消息集为[x+2, x+7], x+2 一次提交的消费位移,
说明己经完成了 x+l 之前(包括 x+l 在内)的所有消息的消费,x+5 表示当前正在处理的位置。如果拉取到消息之后就进行了位移提 ,即提交了x+8,那么当前消费 x+5 的时候遇到了异常,在故障恢复之后,我们重新拉取的消息是从 x+8 开始的。也就是说, x+5至x+7 之间的消息并未能被消费,如此便发生了消息丢失的现象。

再考虑另外 种情形,位移提交的动作是在消费完所有拉取到的消息之后才执行的,那么当消费 x+5 的时候遇到了异常,在故障恢复之后,我们重新拉取的消息是从 x+2 开始的。也就是说, x+2至x+4 之间的消息又重新消费了 遍,故而又发生了重复消费的现象

其次还有更复杂的情况,例如提交回退,比如第 次的位移提交的位置为 x+8 ,而下一次的位移提交的位置为 x+4。

kakfa的消费者位移量的提交分为自动提交手动提交默认位自动提交。在默认的方式下,消费者每隔5秒会将拉取到的每个分区中最大的消息位移进行提交。自动位移提交的动作是在 poll()方法的逻辑里完成的,在每次真正向服务端发起拉取请求之前会检查是否可以进行位移提交,如果可以,那么就会提交上一次轮询的位移。
Kafka 消费的编程逻辑中位移提交是一大难点,自动提交消费位移的方式非常简便,它免去了复杂的位移提交逻辑,让编码更简洁。但随之而来的是重复消费和消息丢失的问题。假设刚刚提交完一次消费位移,然后拉取一批消息进行消费,在下一次自动提交消费位移之前,消费者崩溃了,那么又得从上一次位移提交的地方重新开始消费,这样便发生了重复消费的现象。我们可以通过减小位移提交的时间间隔来减小重复消息的窗口大小,但这样并不能避免重复消费的发送,而且也会使位移提交更加频繁。
按照一般思维逻辑而言,自动提交是延时提交,重复消费可以理解,那么消息丢失又是在什么情形下会发生的呢?我们来看-下图7中的情形 拉取线程A不断地拉取消息并存入本地缓存,比如在BockingQueue 中,另一个处理线程B从缓存中读取消息并进行相应的逻辑处理。假设目前进行到了第 y+l 次拉取,以及第m次位移提交的时候,也就是 x+6 之前的位移己经确认提交了,处理线程B却还正在消费x+3的消息 此时如果处理线程B发生了异常,待其恢复之后会从第 此位移提交处,也就是 x+6 的位置开始拉取消息,那么 x+3至x+6 之间的消息就没有得到相应的处理,这样便发生消息丢失的现象。

图7

自动位移提交的方式在正常情况下不会发生消息丢失或重复消费的现象,但是在编程的世界里异常无可避免,与此同时,自动位移提交也无法做到精确的位移管理。在 Kafka 中还提供了手动位移提交的方式,这样可以使得开发人员对消费位移的管理控制更加灵活。很多时候并不是说拉取到消息就算消费完成,而是需要将消息写入数据库、写入本地缓存,或者是更加复杂的业务处理。在这些场景下,所有的业务处理完成才能认为消息被成功消费,手动的提交方式可以让开发人员根据程序的逻辑在合适的地方进行位移提交。

再均衡

再均衡是指分区的所属权从一个消费者转移到另 一消费者的行为,它为消费组具备高可用性和伸缩性提供保障,使我们可以既方便又安全地删除消费组内的消费者或往消费组内添加消费者。不过在再均衡发生期间,消费组内的消费者是无法读取消息的。 也就是说,在再均衡发生期间的这一小段时间内,消费组会变得不可用。 另外当 一个分区被重新分配给另 一个消费者时, 消费者当前的状态也会丢失。 比如消费者消费完某个分区中的 一部分消息时还没有来得及提交消费位移就发生了再均衡操作, 之后这个分区又被分配给了消费组内的另 一个消者,原来被消费完的那部分消息又被重新消费 一遍,也就是发生了重复消费。一般情况下,应尽量避免不必要的再均衡的发生。

该文章是我读了深入理解kafka核心设计与实践原理的感受和笔记希望可以帮到大家

上一篇下一篇

猜你喜欢

热点阅读