Kafka 进阶:Producer & Broker & Consumer

1. Producer

Producers 负责生产消息。
消息成功写到 topic 后,broker 会返回 producer 消息的 topic, partition & the offset of the record within the partition。

1.1 Send

发送方式有两种:

  • Synchronous send
  • Asynchronous send

1.2 Retry

消息可能因为一些异常原因写失败,异常分为两类:

  • Retriable errors: KafkaProducer 针对这种异常,可以自动的发起重试。全部逻辑隐藏在 send 方法中,开发人员不需要人工干预
    • a connection error can be resolved because the connection may get reestablished.
    • A “no leader” error can be resolved when a new leader is elected for the partition.
  • Nonretriable errors: 这种错误没法通过重试修复,会直接抛异常,需要开发人员处理
    • message size too large error

1.3 Acks

acks 参数控制 producer 认为 message 写成功之前必须接收到 partition 成功写入的副本数(针对 replicas)。可以把acks 理解为用来控制数据备份时的一致性强弱的

当配置 acks 为:

  • 0: producer 不会等待 broker 的写成功回复,producer 发完 request 直接 return,把操作权交给 application developer,这种配置可能造成消息丢失
  • 1: leader replica 成功写消息后,broker 响应写成功通知
    如果 partition leader 所在的 broker crash 了,而新的 leader 还没有选举出来,则 producer 会收到 error 的 response 并发起 retry。
    如果 partition leader 写成功(响应 producer 成功)后 crash 了,而一个还没有同步到 message 的 replica 被选为新的 leader,那这条消息就丢失了。
  • all: request 将存在 buffer 中,直到 leader 观察到所有的 follower replicas 都备份完 messages,才响应 producer。优点:数据一致性强;缺点:性能差。

2. Brokers and Clusters

一个 Kafka 的服务器叫做一个 broker。broker 接受 producer 传递过来的 messages,store messages 到指定的 partition 中,并分配 offsets。它还接受 consumer 发过来的 poll messages request & heartbeats request

broker 的 metadata 在 zookeeper 中维护。每一个 broker 都配置有自身唯一的 id,当 broker start 时,broker 将自身的 id 注册到 zk 中(通过写一个 ephemeral node),如果已经存在一个相同 ID 的 ephemeral node,则会报错。

当 broker 和 zk 断掉连接后(broker stop / network partition / long garbage-collection pause),broker 启动时创建的 ephemeral node 将自动被 zk 删除。

当完全丢失 broker & 删除对应的 ephemeral node 后,重启一个具有相同 id 的新 broker,该 broker 将替代丢失的旧 broker,接受原 broker 相同的 partitions & topics。

2.1 The Controller

一组 brokers 可以搭建成一个 cluster。在 cluster 中有一个 broker 担任 cluster controller,一般是第一个加入 cluster 的 broker 担任 controller,它会在 zk 中创建一个名叫 /controller 的 ephemeral node。

当 controller broker stop or loses connectivity to zk 时,它创建的 /controller node 会被 zk 删除。cluster 中别的 brokers 将被通知 controller 丢失,剩下的 brokers 继续抢占 /controller node,第一个写成功的成为新的 controller。

controller broker 除了承担普通的 broker 功能外,还负责 partition leaders 的选举。如果 controller broker 发现有别的 broker 离开 cluster(通过监听 zk 的相关路径 node)时,那么所有存在于丢失 broker 上的 leader partitions 需要新的 leader,controller 负责选择一个 partition 作为 leader,并通知给各个 brokers partitions。新的 partition leaders 明确自己的职责,followers 则明确自己需要同步的 new leader。

The controller uses the epoch number to prevent a “split brain” scenario where two nodes believe each is the current controller.

2.2 Multiple Clusters

The replication mechanisms within the Kafka clusters are designed only to work within a single cluster, not between multiple clusters.
The Kafka project includes a tool called MirrorMaker, used for this purpose.

2.3 Zookeeper

kafka with zookeeper.jpg

Kafka 用 Zookeeper 来维护 broker cluster,存储 brokers, topics, partitions 的 metadata。
Consumer 的 metadata 在 kafka v0.9 之前的版本中,是通过 Zookeeper 维护。但是在 v0.9 之后,可以选择通过 zookeeper 管理,也可以选择通过 kafka brokers 管理,因为频繁的读写 offsets 对 zk 的压力较大,所以推荐通过 Kafka broker 管理。

3. Replication

前段时间一直在系统性的学习分布式存储的知识,某种程度上来说,Kafka, Redis, MySQL Cluster, Zookeeper 等都可以理解为分布式存储的实现。而分布式存储中的核心实现方法就是 partition & replication。前面我们介绍了 Kafka 的 topic partition,现在来了解下 replication。

为了实现系统的可靠性(availability) & 耐用性(durability),我们可以对 partition 做 replicated。kafka 中的 data 按 topics 组织,每个 topic 可以做 partition,每个 partition 可以有多个 replicas。

replicas 有两种角色:

  • leader replica: 每个 partition 有一个 leader
    • 对该 partition 的所有读写操作(producer, follower replicas, consumer)都是在 leader 节点上完成的,这也是保持 consistency 的一种方式
    • leader 还知道每个 followers 的同步进度
  • follower replica: 除 leader 外的,都是 followers,followers 不直接承担 client 的读写任务。它们的唯一工作就是通过向 leader 发 fetch request 备份 messages。

consumer.poll / replica fetch messages 时,会把自身已有的最大 offset 带给 leader 来获得准确的 messages。当 leader crashes 时,其中一个拥有最多消息(最大 offsets)的 followers 将变成 leader。

4. Consumer

consumers 消费消息。同时 consumer 会跟踪上报自己已消费消息的 offset(kafka 的每个消息在 topic 的 partition 中都有一个唯一的 offset)。
consumers 是以 consumer group 的形式工作的。group 保证每个 partition 只能被一个 consumer 消费,换言之,group 中的 consumer 消费互不相同的 partition。
一个 consumer group/ consumers 可以(通过正则表达式)订阅多个 topics,当新增满足正则表达式的 topic 时,能自动读取到该 topic 的 msg 。

4.1 Consumer Groups

consumer groups 的可能组织结构有:


consumer group.jpg
consumer group2.jpg
consumer group3.jpg
consumer group4.jpg

由于 consumer 经常会做一些高延迟的操作,例如写数据库、分析数据等,consumer 的消费能力可能会小于 producer 的生成能力。

分析以下场景:

  • 如果 partition count > consumer count,可以往 consumer group 中加更多的 consumers 来分担负载,提升消费能力
  • 如果 partition count < consumer count,多出来的 consumers 会闲置,需要增加 partition 的数量。但是这带来的问题是:相同 key 的 messages 在增减 partition count 前后可能分配到不同的 partition 中
    所以,为 topic 创建 partitions 时要预留足够的个数。这样当将来负载变大时,可以方便的通过添加 consumers 来分流。

上面我们提到的都是一个 consumer group 对 topic 的消费。很多情况下,同一个 topic 的消息会有多个不同的应用(user cases)感兴趣(每一个 use case 都能拿到该 topic 的所有 messages),这时就需要为不同的 user case 创建不同的 consumer group,即有多个 consumer groups 消费同一个 topic。


consumer group5.jpg

4.2 Partition Rebalance

发生一下情况时,需要对 consumer group 进行 partition rebalance:

  • 当 consumer group 的消费能力不足时,增加 consumer
  • 当 consumer crash /network failure 时,移除 consumer
  • 管理员增加新的 partition

通过对 partition rebalance 的支持,Kafka 具备了 high availability & scalability。但是正常情况下,尽量避免 partition rebalance。因为:

  • consumer 不能消费消息,consumer group 会有一个短暂的不可用期
  • 当 partition 从原先的 consumer 移到新的 consumer 时,原先 consumer 丢失了它当前处理的状态

设计阶段,就要把由于 rebalance 引发的潜在的消息重复处理的情况考虑进去。

4.3 Group coordinator & Group leader

关于 consumers 的维护,有两个重要的概念:group coordinator & group leader:

  1. group coordinator: 是特殊的 broker。consumers poll 消息 & commit 消费消息记录时,会发送 heartbeats 到 group coordinator 来同时告知自己的健康状况。
  2. group leader: 第一个加入 consumer group 的 consumer 就是该 group 的 group leader。group coordinator 会把 consumers 列表发给 group leader 来维护。group leader 负责 assign & reassign partitions。

如果 consumer crashed/network failure,长时间没有发送 heartbeats 到 group coordinator 时,group coordinator 会认为该 consumer 失联,并通知 group leader rebalance partition,group leader 将 rebalance 的结果通知 group coordinator,由 coordinator 来通知 consumers 新的 partitions 关系。

4.4 Poll loop

consumer 中的核心功能几乎都在 consumer.poll() 方法中。poll(timeout) 通过 timeout 参数控制 poll 的阻塞等待数据时间。如果 timeout = 0,则立即返回,无论是否有新消息。timeout 的值是需要根据自身业务设置的。但是它不仅仅是从 broker 中读取消息:

  1. 初始调用 poll() 时,会去找 groupCoordinator & 加入 consumer group & 接收 partition assignment
  2. poll 内部负责处理 partition rebalance
  3. 发送 heartbeat: 当 consumer 停止 poll() 时,会停止发 heartbeat,被 group coordinator 认为 fail,把分配给它的 partitions 分配给 consumer group 中别的 consumer。

所以:

  • consumer 需要持续 poll 数据
  • consumer 处理数据的过程要越快越好,避免由于长时间不发 heartbeat,引起宕机误判

4.5 Commits and Offsets

Kafka 不像大多数 JMS queue 那样,broker 不主动跟踪 consumer 的 ack,而是通过 consumer 发起 commit 来更新最新的 offset 到 _consumer_offsets topic 中

consumer.poll()时, broker 会返回还没有消费的消息记录,消息中带有自身的 offset。
consumer 通过 commit 动作发送一个带有 partition offset 的 message 到 kafka broker 的特殊 topic(__consumer_offsets topic) 来更新 offset。

consumer 什么时候 commit 该消息呢?

Automatic Commit

配置 enable.auto.commit=true,consumer 会每隔一个 interval (默认每隔5s)自动提交 consumer 通过 poll() 收到的最大的 offset。
automatic commits 也是通过 poll loop 来实现的。每次 poll, consumer 都会自动检查是否到时间执行一次 commit 来提交最近一次 poll() 获得的最大的 offsets。

当 consumer crashes / new consumer 加入 consumer group,会触发 rebalance。在 rebalance 后,每个 consumer 被分配一组新的 partitions,并获取到最新的 committed offset of each partition 来继续工作。
但是考虑以下情况:假设配置每隔 5s commit the latest offset,上次提交 2s 后,发生了 rebalance,所有 consumers 获取到之前最近的 offsets,但这个 offset 其实是 2s 前的,这 2s 间到达 consumers 的消息将会被处理两次。
可以配置较小的 interval 来减少重复消费的消息,但是本质上无法完全避免。

可以看出,操作 committed offset 的位置,是可能发生以下情况:

  • 重复读取 & 消费消息
  • 遗漏处理消息

Commit Current Offset

如果想对 offset 的控制更准确,配置 auto.commit.offset=false,手动 commit offset。

需要开发人员手动调用 commitSync(),将把 poll() 返回的最新的 offset,建议:

  • 在 client 处理完 poll 回的所有数据后,再执行该方法,否则会冒着丢失消息的风险
  • 面对 rebalance 时,仍然存在重复消费消息的情况

Asynchronous Commit

commitSync() 会阻塞应用,直到收到 broker 的明确响应。这将很大的影响系统吞吐。可以考虑使用异步 consumer.commitAsync()

commitSync() 内部有 retry,如果遇到 retriable failure,会持续重试,影响性能,如果遇到 nonretriable failure,则会直接 commit fail。// todo retry count
commitAsync() 没有 retry,之所以不支持 retry,是因为它本身是 async 的、非阻塞的。如果失败了又重试,可能会把这段时间发生的更新的 commit 的数据修改回去。当然了,如果真想重试,是可以找到解决方案的,如记录一个全局单调递增的 sequence number,重试前检查如果 offset 小于该 number,则取消 retry。

Combining Synchronous and Asynchronous Commits

对于手动控制 offset 的情况,commitAsync() & commitSync() 可以结合使用。正常情况下使用 async,提高性能,并且偶尔由于网络原因发生的失败也不需要 retry,一般都会在接下来的 commit 中成功,等待服务停止消费时,调用 sync,确保最终正确提交 offset。

Commit Specified Offset

commitSync() & commitAsync() 存在一个问题,只能在对 batch messages 全部处理完后,将最大的 offset 提交,无法做到更细粒度的控制。当处理 batch messages 的耗时很长,或者 batch 的消息个数很多时,如果在消费过程中发生了 rebalance,这次 poll 获取的所有 messages 都需要重新处理一次。

commitSync() & commitAsync() 都提供了带参数的方法,允许我们根据业务在消费 batch messages 的过程中按需要提交 offset。

4.6 Rebalance Listeners

在发生 partition rebalance 时(可能处在 processing batch messages 间隙),consumer 需要做一些 cleanup work,包括对正在处理的消息的收尾工作,对文件、数据库连接等的管理。我们可以通过在调用 consumer.subscribe() 方法中传入自定义的 ConsumerRebalanceListener 来实现。

ConsumerRebalanceListener 有两个方法需要实现:

  • onPartitionsRevoked: 执行 rebalance 前的收尾工作,consumer 停止处理旧 messages 后 & rebalance 发生前系统调用该方法。可以通过该方法来 commit client 已处理的 offsets,这样能 commit 准确的 offsets
  • onPartitionsAssigned: 执行 rebalance 后的初始化工作,partitions reassigned 后 & consumer 开始处理新 messages 前调用该方法

Consuming Records with Specific Offsets

seek(TopicPartition partition, long offset)
seekToBeginning(TopicPartition tp)
seekToEnd(TopicPartition tp)

Standalone Consumer: Use a Consumer Without a Group

有些情况下,consumer 会需要指定消费某些具体的 partitions, 而不是 join consumer group,由 consumer group 分配 partition & rebalance。可以调用 consumer.assign() 来实现该需求。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容