一文了解Kafka的消息收集器RecordAccumulate

本文详细介绍了KafkaProducer端的RecordAccumulator组件,包括其作用、工作原理以及如何通过buffer.memory和max.block.ms参数优化性能。重点讲解了ProducerBatch和ProducerRecord的区别,以及BufferPool在内存管理中的角色。
摘要由CSDN通过智能技术生成

 原创

爪哇缪斯2023-09-06 10:02:46博主文章分类:面试题精讲©著作权

文章标签kafka消息中间件java面试文章分类Java后端开发yyds干货盘点

〇、前言

在上一篇文章《连Producer端的主线程模块运行原理都不清楚,就敢说自己精通Kafka》中,我们介绍了Main Thread的工作原理,那么在本篇文章中,我们继续介绍第二部分内容:RecordAccumulator

在介绍原理之前,大家再重温一下Producer端的整体架构,图示如下所示:

一文了解Kafka的消息收集器RecordAccumulate_面试

这个图看不懂没有关系,我们会在介绍Producer端原理时一一介绍每个部分的含义及其所复杂的功能。

一、RecordAccumulator

在上文中,我们介绍了主线程(Main Thread)的执行流程,当我们使用KafkaProducer发送消息的时候,消息会经过拦截器(Interceptor)、序列化器(Serializer)和分区器(Partitioner),最后会暂存到消息收集器(RecordAccumulator)中,那么,本节就来针对其进行介绍。

RecordAccumulator的主要作用是暂存Main Thread发送过来的消息,然后Sender Thread就可以从RecordAccumulator中批量的获取到消息,减少单个消息获取的请求次数,提升性能效率。通过参数buffer.memory可以设置缓存大小(默认32M)。

properties.put(ProducerConfig.BUFFER_MEMORY_CONFIG, 3210241024);

由于RecordAccumulator的缓存空间有限,如果空间被占满,那么当我们再次调用KafkaProducer的send(…)方法的时候,就会出现阻塞(默认60秒,可以通过参数max.block.ms来配置),如果阻塞超时,则会抛出异常。

properties.put(ProducerConfig.MAX_BLOCK_MS_CONFIG, 60*1000);

在RecordAccumulator中,我们通过getOrCreateDeque(...)方法来创建存储消息的数据结构,即:存储ProducerBatch实例对象的双向队列Deque;源码如下所示:

private final ConcurrentMap<TopicPartition, Deque<ProducerBatch>> batches; // 主题分区:双向队列

private Deque<ProducerBatch> getOrCreateDeque(TopicPartition tp) {
    Deque<ProducerBatch> d = this.batches.get(tp);
    if (d != null)
        return d;
    d = new ArrayDeque<>(); // 创建双向队列
    Deque<ProducerBatch> previous = this.batches.putIfAbsent(tp, d);
    if (previous == null)
        return d;
    else
        return previous;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.

其对应关系是通过一个主题分区对应双向队列Deque<ProducerBatch>,维护在batches中的,如下图所示:

一文了解Kafka的消息收集器RecordAccumulate_java_02

这时可能会有同学问?我记得调用KafkaProducer发送消息的时候,我们发送的是ProducerRecord实例对象,怎么在Deque双向队列中存储的是ProducerBatch实例对象,他们两个有啥区别呢?ProducerRecord是我们使用KafkaProducer发送消息时拼装的单条消息,而ProducerBatch可以看做是针对一批消息进行的封装,因为会在RecordAccumulator中执行tryAppend方法将一批消息拼装在一起,可以减少网络请求次数从而提升吞吐量。

一文了解Kafka的消息收集器RecordAccumulate_面试_03

Kafka通过ByteBuffer来实现字节形式的网络传输,为了减少频繁创建/释放ByteBuffer所造成的资源消耗,Kafka还提供了缓冲池(BufferPool)来实现ByteBuffer的回收,再其内部维护了Deque<ByteBuffer> free变量来保存空闲ByteBuffer,还提供了Deque<Condition> waiters变量来保存阻塞等待中的线程。

如果待分配的size等于缓冲池中ByteBuffer的大小(可由batch.size参数进行配置,默认为16Kb),则直接从free队列中拿出空余的ByteBuffer供其使用;否则,判断如果缓冲池中空闲ByteBuffer的内存总和加上非缓冲池内存大小是大于待分配size的,则采用非缓冲池加上缓冲池混合释放内存的方式进行内存分配。代码如下所示:

一文了解Kafka的消息收集器RecordAccumulate_面试_04

关于batch.size参数,除了可以影响BufferPool中缓存的ByteBuffer是否被立刻复用之外,还与创建ProducerBatch有关。当我们通过KafkaProducer发送一条由ProducerRecord封装的消息,并交由RecordAccumulate处理时,会执行如下步骤:

1】根据主题分区寻找对应的双向队列Deque<ProducerBatch>,从中获取ProducerBatch;

2】如果这个ProducerBatch还有剩余空间,则直接写入;如果无法写入,则继续执行如下逻辑;

3】如果待保存的消息size小于等于batch.size,则创建batch.size大小的ProducerBatch,当使用完毕后,交由BufferPool管理复用;

4】如果待保存的消息size大于batch.size,那么就创建消息size大小的ProducerBatch,这段内存区域不会被复用。

今天的文章内容就这些了:

写作不易,笔者几个小时甚至数天完成的一篇文章,只愿换来您几秒钟的 点赞 & 分享 。

更多技术干货,欢迎大家关注公众号“爪哇缪斯” ~ \(o)/ ~ 「干货分享,每天更新」

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值