理解Apache kafka 的设计元素和原则(二)

本文探讨了Kafka消息系统的生产者负载均衡策略、消息异步发送机制及消费者拉取模式等核心设计思想。深入剖析了生产者如何直接向分区领导者发送消息以实现负载均衡,以及消费者如何通过拉取消息控制消费进度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

生产者

负载均衡

生产者将消息直接发送给分区的领导者所在的broker,而不经过任何中间路由层。为了实现此功能,所有的kafka节点
可以响应关于那些服务器处于活跃状态,一个分区领导者在给定时间的具体位置等请求来支持生产值适当的指导其请求。
kafka由客户端选择发送消息的分区。这可以随机选择分区,实现一种随机负载均衡,或者也可以通过语义分区功能实现。我们放开语义分区接口来允许用户制定密钥来分区并使用它来作为此分区的hash。例如说,选择的密钥是用户的ID,那么这个用户发送的数据都将发送至同一个分区。反之,消费者也可以对消息的消费进行控制。

异步发送

批处理是高效的重要驱动之一,为了支持批处理操作,kafka生产值需要将大量数据积累在缓存中,然后一次性发送出大量消息。批处理可以配置其最大缓存消息容量和最大缓存时间,这种缓冲是可配置的,允许折中少许额外的延迟换取更大的吞吐量

消费者

kafka消费者通过发送fetch请求给分区leader所在的broker来消费消息。消费者在请求日志中指定偏移并从这个位置开始获取下一段消息日志。因此消费者可以控制消费消息的偏移量,在需要的情况下可以倒带重新消费历史消息。

push vs pull

最初,我们考虑过应该有消费者从broker拉取数据还是有broker向消费者推送数据这个问题。在这个问题上, kafka采用更传统的更广为应用的设计:数据从生产者处发送给broker,并有消费者从broker处拉取。一些日志中心的系统,例如scribe和apache flume等采用的是截然不同的基于推送的方式,这两种方式各有优劣,然而,基于推送的系统在多个消费者的时候,broker将很难控制消息的传输速率。目标通常是消费者能够以最快的速率消费数据,而在推送系统中,当生产速率超过消费速率时(实质上是拒绝服务攻击),消费者往往会不知所措。在基于拉取的系统中,消费可以落后于生产速率,只需要在可以情况下赶上,这使得消费者在不堪重负的情况下可以指示某种退避协议来缓解压力,同时,传输速率能够充分别利用(从不过渡利用),因此在这个问题上,我们采用传统的拉取模式。
拉取系统的另一个有点在于它适用于发送大量的批量消息给消费者。一个基于推送的系统必须在立即发送和积累数据批量发送两种方式中做出选择,并稍后在不知道消费者是否能够立即消费的情况下发送消息。如果倾向于更低的延迟,将会导致非常浪费的方式:一次只发送一条消息。基于拉取的设计解决了这个问题,消费者总是一次性拉取记录的偏移后面的所有消息(或者有配置最大拉取容量)。因此,可以再不增加不必要的延迟的情况下获取最大的吞吐量。
简单的拉取系统有一个缺陷,就是当broker中没有数据时,消费者可能会在一直处于循环等待消息的状态,为了避免这种情况,我们在拉取请求中设置了参数,允许消费者请求以长轮询的方式阻塞等待数据的到达(可配置指定消息大小,直到给定数量的字节可用以确保大传输大小)
可以想象另外一种只允许端对端拉取的设计。生产者在本地写本地日志,broker在消费者向其拉取消息时从生产者处拉取数据。这是被经常提起的一种类似的“存储转发”生产者。这很奇妙,但是我们认为这不是很适合我们目标应用场景:数以千计的生产者。我们运行大规模持久数据系统的经验让我们认为这个系统中诸多应用中涉及的几千个磁盘将会使得事情变得不可靠,这将会是一场噩梦。在实践中,我们发现我们可以在不需要生产者持久化的情况下大规模运行带有强大SLA的管道。

消费者偏移

定义:保存哪些消息已经被消费的轨迹数据,消息传递系统的关键性能点之一。
绝大部分的消息系统在broker上保存消息的消费偏移数据。因为当消息被发送给消费者的时候,broker 可以再本地立刻记录或者等待消费者返回后在记录偏移量。这是一个相当直观的方式,当用来存储消息的数据结构规模变小,这个方式也是相当合适,因为broker知道哪些数据被消费过然后将其删除,将数据量维持在一个较小的规模
这种方式一个不太明显但相当重要的问题是如何统一协调 broker 和 消费者之间对于偏移量的记录。如果broker在每次发送出消息后就立即记录下偏移量,那么如果之后consumer 消费该消息失败的话,这条消息就会丢失。
为了解决这个问题,很多消息系统增加了一个反馈机制,broker 发送出消息后,将该消息标记为 sent 状态,然后等待consumer的回应,如果消费成功,broker 就将消息标记为 consumed。这个策略修复了消息丢失的问题,但是也导致了一些其他问题
其一、如果消费者消费了该消息,但是在返回ack之前错误了,就会导致该消息被消费两次
其二、影响性能,一次消息的发送中,broker必须维持多种状态,(先锁住消息,防止被发送第二次,收到ack后,在标记为consumed,表明可被清除)
其三、如何处理被标记sent,但是没有被返回ack的消息

kafka的策略迥然不同,主题被分割成多个顺序的分区,在给定的时间内,一个分区被同一个消费者组中的一个consumer消费,这意味着,一个消费者在一个分区中的位置也就是偏移量(每个分区同时只能被一个消费者消费)。消费者所在分区的位置也就能标识哪些消息被消费了,哪些还没被消费。这种等价于 ack形式的成本非常低廉(实现的前提就是 主题被分隔成多个 顺序写入 的分区)

这种方式还有个额外的好处,就是消费者可以主动退回的之前的偏移量重新消费消息。这违反了队列的原则,但是对于诸多消费者来说这是一个必不可少的特性。比如说,消费者在消费了一些数据后才发现了bug,这时,消费者就可以会退到过去的消息

离线数据加载

可扩展持久性允许消费者定期消费,例如定期批量数据加载,定期将数据加载到离线系统中
在hadoop的案例上,我们通过多个独立的map任务,并行加载数据。每个节点/主题/分区的组合都允许完全并行加载。由Hadoop提供任务管理,失败的任务可以无危险(无重复数据)的重启—-他们将在上次失败的位置重启。

参考文档

apache kafka官方文档

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值