摘要:发送消息的时候, 当Broker挂掉了,消息体还能写入到消息缓存中吗?

本文分享自华为云社区《图解Kafka Producer 消息缓存模型》,作者:石臻臻的杂货铺。

在阅读本文之前, 希望你可以思考一下下面几个问题, 带着问题去阅读文章会获得更好的效果。

  1. 发送消息的时候, 当Broker挂掉了,消息体还能写入到消息缓存中吗?
  2. 当消息还存储在缓存中的时候, 假如Producer客户端挂掉了,消息是不是就丢失了?
  3. 当最新的Producer Batch还有空余的内存,但是接下来的一条消息很大,不足以加上上一个Batch中,会怎么办呢?
  4. 那么创建Producer Batch的时候,应该分配多少的内存呢?

什么是消息累加器Record Accumulator

kafka为了提高Producer客户端的发送吞吐量和提高性能,选择了将消息暂时缓存起来,等到满足一定的条件, 再进行批量发送, 这样可以减少网络请求,提高吞吐量。

而缓存这个消息的就是Record Accumulator类.

上图就是整个消息存放的缓存模型,我们接下来一个个来讲解。

消息缓存模型

上图表示的就是 消息缓存的模型, 生产的消息就是暂时存放在这个里面。

  1. 每条消息,我们按照TopicPartition维度,把他们放在不同的Deque<ProducerBatch> 队列里面。
    TopicPartition相同,会在相同Deque<ProducerBatch> 的里面。
  2. ProducerBatch : 表示同一个批次的消息, 消息真正发送到Broker端的时候都是按照批次来发送的,
    这个批次可能包含一条或者多条消息。
  3. 如果没有找到消息对应的ProducerBatch队列, 则创建一个队列。
  4. 找到ProducerBatch队列队尾的Batch,发现Batch还可以塞下这条消息,则将消息直接塞到这个Batch中
  5. 找到ProducerBatch队列队尾的Batch,发现Batch中剩余内存,不够塞下这条消息,则会创建新的Batch
  6. 当消息发送成功之后, Batch会被释放掉。

ProducerBatch的内存大小

那么创建ProducerBatch的时候,应该分配多少的内存呢?

先说结论: 当消息预估内存大于batch.size的时候,则按照消息预估内存创建, 否则按照batch.size的大小创建(默认16k).

我们来看一段代码,这段代码就是在创建ProducerBatch的时候预估内存的大小

RecordAccumulator#append

  /**
* 公众号: 石臻臻的杂货铺
* 微信:szzdzhp001
**/
// 找到 batch.size 和 这条消息在batch中的总内存大小的 最大值
int size = Math.max(this.batchSize, AbstractRecords.estimateSizeInBytesUpperBound(maxUsableMagic, compression, key, value, headers));
// 申请内存
buffer = free.allocate(size, maxTimeToBlock);
  1. 假设当前生产了一条消息为M, 刚好消息M找不到可以存放消息的ProducerBatch(不存在或者满了),那么这个时候就需要创建一个新的ProducerBatch了
  2. 预估消息的大小 跟batch.size 默认大小16384(16kb). 对比,取最大值用于申请的内存大小的值。

原文地址:图解Kafka Producer 消息缓存模型

那么, 这个消息的预估是如何预估的?纯粹的是消息体的大小吗?

DefaultRecordBatch#estimateBatchSizeUpperBound

预估需要的Batch大小,是一个预估值,因为没有考虑压缩算法从额外开销

 /**
* 使用给定的键和值获取只有一条记录的批次大小的上限。
* 这只是一个估计,因为它没有考虑使用的压缩算法的额外开销。
**/
static int estimateBatchSizeUpperBound(ByteBuffer key, ByteBuffer value, Header[] headers) {
return RECORD_BATCH_OVERHEAD + DefaultRecord.recordSizeUpperBound(key, value, headers);
}
  1. 预估这个消息M的大小 + 一个RECORD_BATCH_OVERHEAD的大小
  2. RECORD_BATCH_OVERHEAD是一个Batch里面的一些基本元信息,总共占用了 61B
  3. 消息M的大小也并不是单单的只有消息体的大小,总大小=(key,value,headers)的大小+MAX_RECORD_OVERHEAD
  4. MAX_RECORD_OVERHEAD :一条消息头最大占用空间, 最大值为21B

也就是说创建一个ProducerBatch,最少就要83B .

比如我发送一条消息 " 1 " , 预估得到的大小是 86B, 跟batch.size(默认16384) 相比取最大值。 那么申请内存的时候取最大值 16384 。

内存分配

我们都知道RecordAccumulator里面的缓存大小是一开始定义好的, 由buffer.memory控制, 默认33554432 (32M)

当生产的速度大于发送速度的时候,就可能出现Producer写入阻塞。

而且频繁的创建和释放ProducerBatch,会导致频繁GC, 所有kafka中有个缓存池的概念,这个缓存池会被重复使用,但是只有固定( batch.size)的大小才能够使用缓存池。

PS:以下16k指得是 batch.size的默认值.

原文地址:图解Kafka Producer 消息缓存模型

Batch的创建和释放

1. 内存16K 缓存池中有可用内存

①. 创建Batch的时候, 会去缓存池中,获取队首的一块内存ByteBuffer 使用。

②. 消息发送完成,释放Batch, 则会把这个ByteBuffer,放到缓存池的队尾中,并且调用ByteBuffer.clear 清空数据。以便下次重复使用

2. 内存16K 缓存池中无可用内存

①. 创建Batch的时候, 去非缓存池中的内存获取一部分内存用于创建Batch. 注意:这里说的获取内存给Batch, 其实就是让 非缓存池nonPooledAvailableMemory 减少 16K 的内存, 然后Batch正常创建就行了, 不要误以为好像真的发生了内存的转移。

②. 消息发送完成,释放Batch, 则会把这个ByteBuffer,放到缓存池的队尾中,并且调用ByteBuffer.clear 清空数据, 以便下次重复使用

原文地址:图解Kafka Producer 消息缓存模型

3. 内存非16K 非缓存池中内存够用

①. 创建Batch的时候, 去非缓存池(nonPooledAvailableMemory)内存获取一部分内存用于创建Batch. 注意:这里说的获取内存给Batch, 其实就是让 非缓存池(nonPooledAvailableMemory) 减少对应的内存, 然后Batch正常创建就行了, 不要误以为好像真的发生了内存的转移。

②. 消息发送完成,释放Batch, 纯粹的是在非缓存池(nonPooledAvailableMemory)中加上刚刚释放的Batch内存大小。 当然这个Batch会被GC掉

4. 内存非16K 非缓存池内存不够用

①. 先尝试将 缓存池中的内存一个一个释放到 非缓存池中, 直到非缓存池中的内存够用与创建Batch了

②. 创建Batch的时候, 去非缓存池(nonPooledAvailableMemory)内存获取一部分内存用于创建Batch. 注意:这里说的获取内存给Batch, 其实就是让 非缓存池(nonPooledAvailableMemory) 减少对应的内存, 然后Batch正常创建就行了, 不要误以为好像真的发生了内存的转移。

③. 消息发送完成,释放Batch, 纯粹的是在非缓存池(nonPooledAvailableMemory)中加上刚刚释放的Batch内存大小。 当然这个Batch会被GC掉

例如: 下面我们需要创建 48k的batch, 因为超过了16k,所以需要在非缓存池中分配内存, 但是非缓存池中当前可用内存为0 , 分配不了, 这个时候就会尝试去 缓存池里面释放一部分内存到 非缓存池。

释放第一个ByteBuffer(16k) 不够,则继续释放第二个,直到释放了3个之后总共48k,发现内存这时候够了, 再去创建Batch。

注意:这里我们涉及到的 非缓存池中的内存分配, 仅仅指的的内存数字的增加和减少。

问题和答案

1、发送消息的时候, 当Broker挂掉了,消息体还能写入到消息缓存中吗?

当Broker挂掉了,Producer会提示下面的警告️, 但是发送消息过程中

这个消息体还是可以写入到 消息缓存中的,也仅仅是写到到缓存中而已。

 WARN [Producer clientId=console-producer] Connection to node 0 (/172.23.164.192:9090) could not be established. Broker may not be available

2、当最新的ProducerBatch还有空余的内存,但是接下来的一条消息很大,不足以加上上一个Batch中,会怎么办呢?

那么会创建新的ProducerBatch。

3、那么创建ProducerBatch的时候,应该分配多少的内存呢?

触发创建ProducerBatch的那条消息预估大小大于batch.size ,则以预估内存创建。
否则,以batch.size创建。

还有一个问题供大家思考:

当消息还存储在缓存中的时候, 假如Producer客户端挂掉了,消息是不是就丢失了?

点击关注,第一时间了解华为云新鲜技术~

6张图为你分析Kafka Producer 消息缓存模型的更多相关文章

  1. Kafka生成消息时的3种分区策略

    摘要:KafkaProducer在发送消息的时候,需要指定发送到哪个分区, 那么这个分区策略都有哪些呢? 本文分享自华为云社区<Kafka生产者3中分区分配策略>,作者:石臻臻的杂货铺. ...

  2. Kafka Producer相关代码分析【转】

    来源:https://www.zybuluo.com/jewes/note/63925 @jewes 2015-01-17 20:36 字数 1967 阅读 1093 Kafka Producer相关 ...

  3. 关于高并发下kafka producer send异步发送耗时问题的分析

    最近开发网关服务的过程当中,需要用到kafka转发消息与保存日志,在进行压测的过程中由于是多线程并发操作kafka producer 进行异步send,发现send耗时有时会达到几十毫秒的阻塞,很大程 ...

  4. 一张图了解Spring Cloud微服务架构

    Spring Cloud作为当下主流的微服务框架,可以让我们更简单快捷地实现微服务架构.Spring Cloud并没有重复制造轮子,它只是将目前各家公司开发的比较成熟.经得起实际考验的服务框架组合起来 ...

  5. Kafka producer介绍

    Kafka 0.9版本正式使用Java版本的producer替换了原Scala版本的producer.本文着重讨论新版本producer的设计原理以及基本的使用方法. 新版本Producer 首先明确 ...

  6. Kafka设计解析(十四)Kafka producer介绍

    转载自 huxihx,原文链接 Kafka producer介绍 Kafka 0.9版本正式使用Java版本的producer替换了原Scala版本的producer.本文着重讨论新版本produce ...

  7. 源码分析 Kafka 消息发送流程(文末附流程图)

    温馨提示:本文基于 Kafka 2.2.1 版本.本文主要是以源码的手段一步一步探究消息发送流程,如果对源码不感兴趣,可以直接跳到文末查看消息发送流程图与消息发送本地缓存存储结构. 从上文 初识 Ka ...

  8. 源码分析Kafka 消息拉取流程

    目录 1.KafkaConsumer poll 详解 2.Fetcher 类详解 本节重点讨论 Kafka 的消息拉起流程. @(本节目录) 1.KafkaConsumer poll 详解 消息拉起主 ...

  9. 干货 | 45张图庖丁解牛18种Queue,你知道几种?

    在讲<21张图讲解集合的线程不安全>那一篇,我留了一个彩蛋,就是Queue(队列)还没有讲,这次我们重点来看看Java中的Queue家族,总共涉及到18种Queue.这篇恐怕是市面上最全最 ...

随机推荐

  1. Java GUI 简单台球游戏模型

    完成效果: 1 package com.neuedu.test; 2 3 import java.awt.Frame; 4 import java.awt.Graphics; 5 import jav ...

  2. iframe父子页面相互调用方法,相互获取元素

    父页面获取子页面 var childWin = document.getElementById('setIframe').contentWindow;//获取子页面窗口对象 childWin.send ...

  3. 测试提高路线图_tester+

    https://mp.weixin.qq.com/s/30ZT0w164Q3iLdPg4R8org

  4. ESXI 7.0.0 U2 部署

    文章目录 什么是ESXI? ESXi 的优势 功能特性 部署ESXI 创建虚拟机 开始安装 打开浏览器输入ip进行管理 什么是ESXI? ESXI官网:https://www.vmware.com/c ...

  5. GIT学习——天天都在用Git,那么你系统学习过吗?(学习过程)

    你系统学习Git了吗? 学习圣思园张龙老师的Git课程. 使用Mac编程的好处,不是因为Mac长得好看 Git内容学习准备 如果你还没有用Git,就不要写代码了. GitHub仓库的使用. 新员工入职 ...

  6. 『无为则无心』Python面向对象 — 57、类属性和实例属性

    目录 1.类属性 (1)类属性的访问 (2)修改类属性 2.类属性和实例属性区别 1.类属性 (1)类属性的访问 类属性就是 类对象 所拥有的属性,它被 该类的所有实例对象 所共有. 类属性可以使用 ...

  7. [数分笔记]问题1.1 T1

    题目:非负整数a,b使得为整数,求证这个整数必是某一整数的平方.(1988年第29届国际数学奥林匹克竞赛试题) 证明:设k=,k为非负整数 1°a=b k=2a²/(1+a²)=2-2/(1+a²) ...

  8. excel仪表盘制作,商业智能仪表盘的作用

    ​商业仪表盘被称为管理驾驶舱的重要组成部分,无论是管理决策者,还是企业业务流程中的普通员工,都可以利用它来展示分析的结果,让决策更加快速准确,更快地推动业务流程的进展,提高工作效率. 一个明确地了解自 ...

  9. 深度测评,商业智能BI、报表工具谁更好用?

    在很多人入门数据分析师或者投身大数据行业的时候,必然会听到的两个词就是"报表工具"和"BI商业智能"."BI"一词已被更广泛地知晓,但提起B ...

  10. 『无为则无心』Python日志 — 64、Python日志模块logging介绍

    目录 1.日志的作用 2.为什么需要写日志 3.Python中的日志处理 (1)logging模块介绍 (2)logging模块的四大组件 (3)logging日志级别 1.日志的作用 从事与软件相关 ...