更加深入理解Kafka--Producer篇(下)

2024-03-08 14:38

本文主要是介绍更加深入理解Kafka--Producer篇(下),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

批次

积累器在创建批次之前,就在堆上为它预分配一段空间,这段空间用于装载消息。消息最终会顺序落到内存块中形成消息集。批次的逻辑结构如下:
批次逻辑结构.jpg

5.0.1

* MemoryRecords即消息集的抽象,它容纳0到多条Record。
* Record则代表消息在内存中的状态,即按二进制协议格式化之后的消息结构,它是消息集的元素。
* 用户可通过compression.type配置压缩方式,开启压缩可显著增大内存使用率、同时减少网络开销。Compressor负责压缩消息,它的属性appendStream是个包装流,其结构是DataOutputStream—>压缩处理流—>ByteBufferStream。
压缩器.png
5.0.2

批次失效会关闭消息集使其变为只读状态,并引起Compressor关闭:释放全部I/O资源并在开启压缩时在缓冲头部位置填充协议元数据。关闭后缓冲将不再有消息写入,它被回给消息集并flip后等待发送。

数据协议

批次是消息存储的最小物理单元,读取时就只能按批次整块读取,因此如果没有标准数据协议就无法对数据块做反序列化。

Kafka把消息分割成写前日志、协议头和协议体三部分,协议头和协议体合成协议正文。日志标识消息在批次中的相对顺序和原始正文大小;消息头声明CRC、魔数和属性;最后消息体记录追加时间以及key和value值。

消息物理结构.png

5.1.1

CRC即checkSum值,用于校验消息是否完整;魔数用于声明所用协议版本;属性占1个字节即8位,目前只使用了前三位,每一位代表一种压缩协议,为0即不压缩;key和value几乎一致,前4个字节标识内容长度,如果内容为-1,则表示无内容填入。

当开启压缩时,Compressor会对消息集偏移在起始位置预留出报文头长度的位置,在批次关闭后再将报文头相关数据写入,因为正文长度、payload长度以及消息数量都只能在消息只读后确定。报文头加上消息集才是完整的压缩报文。压缩报文结构和消息几乎一致,也分日志和正文两个部分,但是在个别属性上会有细微差异:1)offset分别被用于标识消息数量;2)没有key值,所有key长度都是-1;3)value长度是消息集(压缩后)的长度,payload就是消息集本身。报文头并不会被压缩,因此可以很容易被读取,程序识别报文的长度、压缩协议、版本号以及CRC等属性之后就可以选用合适的方式读取一定长度的消息以及校验批次的完整性。

批次管理

批次创建后会逗留linger.ms时间,它集聚该段时间内归属该分组(区)的消息。如果生产速率特别高又或者有超大消息流入很快将分区打满,则实际逗留时间会低于linger.ms。想象一下极端场景,批次大小默认16k,如果消息以5k、12k间隔发,则内存实际利用率只有(5+12)/(2*16)。

另一方面,积累器挤出前先要做就绪节点检查,挤出动作也只针对leader在这些节点上的分区批次,但节点ready to drain后,可能因为连接或者inflightRequests超限等问题,被从发送就绪列表移除,从而导致这些节点的可发送批次不会被挤出。它们始终占据分组队列的最高挤出优先级,这会导致:1)后追加的消息被积压,即使连接恢复后新入的消息也只能等待顺序处理,整体投递延时猛增。2)批次占据的内存得不到释放,有可能发生雪崩:因为只有追加没有挤出,问题节点的批次有可能占满全部内存空间导致其他正常节点分区无法为新批次申请空间。Kafka提供请求超时timeout.ms解决这个问题,从逗留截止开始计算批次超时则被废弃–释放内存空间并从分组队列移除。

理想状况下,单位时间内追入和挤出应该恰好相等且内存被充分使用。长期观察下调好linger.ms、batch.size、timeout.ms以及batch.size和buffer.memory这几个参数将有助于达到这个目标。

内存管理

消息集内存直接分配在堆上,如果对它不加以限制在消息生产速率足够高时很可能频繁出现fgc乃至oom,另一方面频繁的内存申请和释放操作也很吃系统资源,因此Kafka自建了内存池BufferPool管理内存。

内存池有四个关键属性:totalMemory代表内存池上限,由buffer.memory决定;poolableSize指池化内存块大小,由batch.size设置;free和availableMemory则分别代表池化内存和闲置内存大小。注意free和available的区别,前者是已申请但未使用,后者是未申请未使用,它们之间关系:totalMemory= 可使用空间+已使用空间,可使用空间=availableMemory+free.size()*poolableSize代表。

只有固定大小的内存块被释放后才会进入池化列表,非常规释放后只会增加可用内存大小,而释放内存则由虚拟机回收。因此如果超大消息比较多,依然有可能会引起fgc乃至oom。

积累器通过内存池预分配消息集内存,如果没有足够内存则用户主线程被放入有序队列并进入等待。批在批次done时释放出部分空间,同时唤醒队首线程,如果没有释放出足够的空间则继续进入等待,如果已经释放出足够空间,分配空间且线程出队。
内存池.png

5.3.1

这篇关于更加深入理解Kafka--Producer篇(下)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/787413

相关文章

一文深入详解Python的secrets模块

《一文深入详解Python的secrets模块》在构建涉及用户身份认证、权限管理、加密通信等系统时,开发者最不能忽视的一个问题就是“安全性”,Python在3.6版本中引入了专门面向安全用途的secr... 目录引言一、背景与动机:为什么需要 secrets 模块?二、secrets 模块的核心功能1. 基

Go学习记录之runtime包深入解析

《Go学习记录之runtime包深入解析》Go语言runtime包管理运行时环境,涵盖goroutine调度、内存分配、垃圾回收、类型信息等核心功能,:本文主要介绍Go学习记录之runtime包的... 目录前言:一、runtime包内容学习1、作用:① Goroutine和并发控制:② 垃圾回收:③ 栈和

深入解析 Java Future 类及代码示例

《深入解析JavaFuture类及代码示例》JavaFuture是java.util.concurrent包中用于表示异步计算结果的核心接口,下面给大家介绍JavaFuture类及实例代码,感兴... 目录一、Future 类概述二、核心工作机制代码示例执行流程2. 状态机模型3. 核心方法解析行为总结:三

SpringBoot实现Kafka动态反序列化的完整代码

《SpringBoot实现Kafka动态反序列化的完整代码》在分布式系统中,Kafka作为高吞吐量的消息队列,常常需要处理来自不同主题(Topic)的异构数据,不同的业务场景可能要求对同一消费者组内的... 目录引言一、问题背景1.1 动态反序列化的需求1.2 常见问题二、动态反序列化的核心方案2.1 ht

spring IOC的理解之原理和实现过程

《springIOC的理解之原理和实现过程》:本文主要介绍springIOC的理解之原理和实现过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、IoC 核心概念二、核心原理1. 容器架构2. 核心组件3. 工作流程三、关键实现机制1. Bean生命周期2.

MySQL数据库约束深入详解

《MySQL数据库约束深入详解》:本文主要介绍MySQL数据库约束,在MySQL数据库中,约束是用来限制进入表中的数据类型的一种技术,通过使用约束,可以确保数据的准确性、完整性和可靠性,需要的朋友... 目录一、数据库约束的概念二、约束类型三、NOT NULL 非空约束四、DEFAULT 默认值约束五、UN

Java Stream流使用案例深入详解

《JavaStream流使用案例深入详解》:本文主要介绍JavaStream流使用案例详解,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录前言1. Lambda1.1 语法1.2 没参数只有一条语句或者多条语句1.3 一个参数只有一条语句或者多

深入理解Apache Kafka(分布式流处理平台)

《深入理解ApacheKafka(分布式流处理平台)》ApacheKafka作为现代分布式系统中的核心中间件,为构建高吞吐量、低延迟的数据管道提供了强大支持,本文将深入探讨Kafka的核心概念、架构... 目录引言一、Apache Kafka概述1.1 什么是Kafka?1.2 Kafka的核心概念二、Ka

Java并发编程必备之Synchronized关键字深入解析

《Java并发编程必备之Synchronized关键字深入解析》本文我们深入探索了Java中的Synchronized关键字,包括其互斥性和可重入性的特性,文章详细介绍了Synchronized的三种... 目录一、前言二、Synchronized关键字2.1 Synchronized的特性1. 互斥2.

一文带你深入了解Python中的GeneratorExit异常处理

《一文带你深入了解Python中的GeneratorExit异常处理》GeneratorExit是Python内置的异常,当生成器或协程被强制关闭时,Python解释器会向其发送这个异常,下面我们来看... 目录GeneratorExit:协程世界的死亡通知书什么是GeneratorExit实际中的问题案例