探索Kafka消费者的内部结构


Adobe使用Kafka处理数据的流量规模:310B msg/day300 TB/day IN920 TB/day OUT,今天我们将分享我们在 Kafka Client 内部的专业知识。
Kafka Consumer 是一个从 Kafka 消费消息的客户端。

组成:

  • 消费者元数据——管理消费者所需的元数据:集群中的主题和分区、充当分区领导者的代理节点等。
  • Subscriptions — 跟踪消费者订阅状态
  • 反序列化器——记录键和值反序列化器。反序列化器将字节数组转换为对象。
  • Partition Assignor — 完全限定的类名,表示实现org.apache.kafka.clients.consumer.ConsumerPartitionAssignor的分区分配策略
  • 拦截器——可能改变记录的拦截器
  • Consumer Coordinator — 管理组成员、偏移量
  • 网络客户端——处理对代理的请求
  • Fetcher——从经纪人那里获取成批的记录。

配置 Kafka 消费者
Kafka Consumer 有四个必需的属性:

  • bootstrap.servers — 用于建立与 Kafka 集群的初始连接的主机/端口对列表。格式:“host1:port1,host2:port2,...”
  • key.deserializer — 完全限定的类名,表示实现org.apache.kafka.common.serialization.Deserializer接口的密钥反序列化器。
  • value.deserializer — 完全限定的类名,表示实现org.apache.kafka.common.serialization.Deserializer接口的值反序列化器。
  • group.id — 唯一的消费者组 ID。如果客户端使用 subscribe() 方法来消费消息或使用偏移量管理功能,则需要。

订阅主题
Kafka Consumer 提供了两种订阅主题的方式:通过subscribe()assign()方法每个都有一组不同的受支持功能。
subscribe()

  • 支持订阅主题列表或使用正则表达式
  • 具有消费者故障检测的组成员身份(客户端和服务器端)
  • 动态分区分配
  • 自动或手动偏移管理
  • 每个分区单个消费者(使用标准分区分配器)

assign()
  • 使用主题分区订阅进行更精细的控制
  • 自动或手动偏移管理
  • 每个分区支持多个消费者

Apache Flink 和 Spark 使用assign()来订阅主题并管理主题-分区对在工作人员之间的分布。

消费消息
订阅主题后,用户可以通过调用poll()方法来使用消息。根据订阅方法,在幕后会发生几个调用,在下面的序列图中列出。
使用 subscribe() 消费

subscribe()的情况下,Kafka Consumer 在成为消费者组的活跃成员之前不会消费记录。我们看到一些团队错过了这一点,花时间调试停止消费,而根本原因是不稳定的消费群体

使用 assign()

assign()的情况下,Kafka Consumer 不会调用组成员功能,例如心跳和加入/重新加入消费者组。

提取器Fetcher
无论订阅方式如何,Kafka Consumer 都使用 Fetcher 从 broker 中检索批记录。

Fetcher 将 Producer 发送的压缩批次保存在内存中,并在poll() 上解压缩记录。一旦批次被消耗,它就会从内存中丢弃。fetch.min.bytesfetch.max.wait.ms是用于调整 fetcher 吞吐量或延迟的关键配置。增加字节和时间将导致吞吐量增加并减少更好的延迟。

消费群
当用户使用subscribe()进行消费时,具有相同group.id的消费者将组成一个消费者组,共同消费 topic(s) 消息。Kafka 集群将选举其中一个代理作为Group Coordinator。组协调器负责管理组列表成员、接收心跳、触发组成员更改的重新平衡等。协调器将选举一个消费者作为组领导,并要求在消费者之间进行分区分配。每个分区将只分配一个消费者。

消费者群体再平衡
组成员身份的更改将触发消费者组重新平衡。在重新平衡期间,组长将重新计算当前成员之间的分区分配。重新平衡触发时

  • 消费者加入群组
  • 消费者离开群体
  • 通过max.poll.interval.ms检测到客户端故障
  • 通过session.timeout.ms检测到服务器端故障

可能触发消费者组重新平衡的可能原因列表:
  • 在我们的 OUT 中扩展服务
  • poll() 和长消息处理发生在同一个线程中
  • 组协调员心跳失败
  • JVM 垃圾收集暂停
  • Kubernetes PODS CPU 受到限制
  • Kubernetes 集群升级,导致 POD 被驱逐
  • 网络问题(延迟、丢包等)

消费者分区分配器
Kafka Consumer 提供了几个选项来选择在重新平衡期间如何分配分区。用户可以通过partition.assignment.strategy配置参数来控制它,使用实现org.apache.kafka.clients.consumer.ConsumerPartitionAssignor的完全限定类名的值。开箱即用的 Kafka 提供了以下策略:

  • Range - 停止世界策略,以主题为基础。它可能会产生不平衡的分配。更多细节javadoc
  • RoundRobin——停止世界策略,为相同的订阅统一分配分区。更多细节在javadoc中。
  • Sticky — 停止世界策略,初始分发将接近RoundRobin。尝试最小化重新平衡期间移动的分区,可能会产生不平衡的分配。更多细节在javadoc中。
  • CooperativeSticky —在不停止消费的情况下进行增量再平衡。这与 Sticky 的逻辑相同,但具有增量支持。这种策略可能会产生不平衡的分配。更多细节在javadoc中。