kafka发送大消息

1 kafka消息压缩

kafka关于消息压缩的定义(来源于官网):

此为 Kafka 中端到端的块压缩功能。如果启用,数据将由 producer 压缩,以压缩格式写入服务器,并由 consumer 解压缩。压缩将提高 consumer 的吞吐量,但需付出一定的解压成本。

压缩就是用时间换空间,其基本理念是基于重复,将重复的片段编码为字典,字典的 key 为重复片段,value 为更短的代码,比如序列号,然后将原始内容中的片段用代码表示,达到缩短内容的效果,压缩后的内容则由字典和代码序列两部分组成。解压时根据字典和代码序列可无损地还原为原始内容。通常来讲,重复越多,压缩效果越好。比如 JSON 是 Kafka 消息中常用的序列化格式,单条消息内可能并没有多少重复片段,但如果是批量消息,则会有大量重复的字段名,批量中消息越多,则重复越多,这也是为什么 Kafka 更偏向块压缩,而不是单条消息压缩。

2 kafka的消息压缩类型对比

目前 Kafka 共支持四种主要的压缩类型:Gzip、Snappy、Lz4 和 Zstd。关于这几种压缩的特性。

压缩类型 压缩率 CPU 使用率 压缩速度 带宽使用率
Gzip Highest Highest Slowest Lowest
Snappy Medium Moderate Moderate Medium
Lz4 Low Lowest Fastest Highest
Zstd Medium Moderate Moderate Medium

从上表可知,Snappy 在 CPU 使用率、压缩比、压缩速度和网络带宽使用率之间实现良好的平衡,我们最终也是采用的该类型进行压缩试点。这里值得一提的是,Zstd 是 Facebook 于 2016 年开源的新压缩算法,压缩率和压缩性能都不错,具有与 Snappy(Google 杰作)相似的特性,直到 Kafka 的 2.1.0 版本才引入支持。

3 何时需要压缩

压缩是需要额外的 CPU 代价的,并且会带来一定的消息分发延迟,因而在压缩前要慎重考虑是否有必要。

  • 压缩带来的磁盘空间和带宽节省远大于额外的 CPU 代价,这样的压缩是值得的。
  • 数据量足够大且具重复性。消息压缩是批量的,低频的数据流可能都无法填满一个批量,会影响压缩比。数据重复性越高,往往压缩效果越好,例如 JSON、XML 等结构化数据;但若数据不具重复性,例如文本都是唯一的 md5 或 UUID 之类,违背了压缩的重复性前提,压缩效果可能不会理想。
  • 系统对消息分发的延迟没有严苛要求,可容忍轻微的延迟增长。

4 如何开启压缩

Kafka 通过配置属性 compression.type 控制是否压缩。该属性在 producer 端和 broker 端各自都有一份,也就是说,我们可以选择在 producer 或 broker 端开启压缩,对应的应用场景各有不同。目前没有尝试在broker段开启压缩。

4.1 在broker端开启解压缩

Broker 端的 compression.type 属性默认值为 producer,即直接继承 producer 端所发来消息的压缩方式,无论消息采用何种压缩或者不压缩,broker 都原样存储。、

4.1.1 broker 和 topic 两个级别

在 broker 端的压缩配置分为两个级别:全局的 broker 级别 和 局部的 topic 级别。顾名思义,如果配置的是 broker 级别,则对于该 Kafka 集群中所有的 topic 都是生效的。但如果 topic 级别配置了自己的压缩类型,则会覆盖 broker 全局的配置,以 topic 自己配置的为准。

broker级别: 要配置 broker 级别的压缩类型,可通过 configs 命令修改 compression.type 配置项取值。此处要使修改生效,是否需要重启 broker 取决于 Kafak 的版本,在 1.1.0 之前,任何配置项的改动都需要重启 broker 才生效,而从 1.1.0 版本开始,Kafka 引入了动态 broker 参数,将配置项分为三类:read-onlyper-brokercluster-wide,第一类跟原来一样需重启才生效,而后面两类都是动态生效的,只是影响范围不同,关于 Kafka 动态参数,以后单开博文介绍。从 官网 可以看到,compression.type 是属于 cluster-wide 的,如果是 1.1.0 及之后的版本,则无需重启 broker。

topic级别: topic 的配置分为两部分,一部分是 topic 特有的,如 partitions 等,另一部分则是默认采用 broker 配置,但也可以覆盖。如果要定义 topic 级别的压缩,可以在 topic 创建时通过 --config 选项覆盖配置项 compression.type 的取值,命令如下:

bash 复制代码
sh bin/kafka-topics.sh \
--create \
--topic my-topic \
--replication-factor 1 \
--partitions 1 \
--config compression.type=snappy

也可以通过 configs 命令修改 topic 的 compression.type 取值,命令如下:

bash 复制代码
bin/kafka-configs.sh \
--entity-type topics \
--entity-name my-topic \
--alter \
--add-config compression.type=snappy

4.2 在 Producer 端压缩

跟 broker 端一样,producer 端的压缩配置属性依然是 compression.type,只不过默认值和可选值有所不同。默认值为 none,表示不压缩。直接在代码层面更改 producer 的 config。但需要注意的是,改完 config 之后,需要重启 producer 端的应用程序,压缩才会生效。

代码示例如下:

java 复制代码
public class KafkaProducerTest {


    public static void main(String[] args) {
        String brokerList = "127.0.0.1:9092";

        Properties properties = new Properties();
        properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, brokerList);
        properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
        properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
        properties.put(ProducerConfig.MAX_REQUEST_SIZE_CONFIG,"2097245");
        properties.put(ProducerConfig.COMPRESSION_TYPE_CONFIG,"gzip");
        KafkaProducer<String, String> producer = new KafkaProducer<>(properties);

        String topic = "mytestTopic1";

        int sizeInMb = 2; // 设置字符串大小为2MB
        int sizeInBytes = sizeInMb * 1024 * 1024; // 转换为字节数
        StringBuilder largeString = new StringBuilder(sizeInBytes);
        largeString.append(":");
        for (int i = 0; i < sizeInBytes; i++) {
            largeString.append("A"); // 使用大写字母"A"来构建字符串
        }
        String msg = largeString.toString();
        try {
            for (int i = 0; i < 100; i++) {
                String msg1 = i+msg;
                producer.send(new ProducerRecord<>(topic, msg1));
                Thread.sleep(500);
            }
        }catch (Exception e){
            e.printStackTrace();
        }


    }
}

上面示例特意制造了一个大字符串作为消息,测试压缩,需要注意的是,配置压缩的时候同时也需要配置消息的最大值。即:max.request.size。

5 解压缩

可能发生解压的地方依然是两处:consumer 端和 broker 端。

consumer端: consumer 端发生解压的唯一条件就是从 broker 端拉取到的消息是带压缩的。此时,consumer 会根据 recordBatchcompressionType 来对消息进行解压。

broker端: broker 端是否发生解压取决于 producer 发过来的批量消息 recordBatch 是否是压缩的:如果 producer 开启了压缩,则会发生解压,否则不会。原因简单说下,在 broker 端持久化消息前,会对消息做各种验证,此时必然会迭代 recordBatch,而在迭代的过程中,会直接采用 recordBatch 上的 compressionType 对消息字节流进行处理,是否解压取决于 compressionType 是否是压缩类型。关于这点,可以在 LogValidatorvalidateMessagesAndAssignOffsets 方法实现中可以看到,在 convertAndAssignOffsetsNonCompressedassignOffsetsNonCompressedvalidateMessagesAndAssignOffsetsCompressed 三个不同的分支中,都会看到 records.batches.forEach {...} 的身影,而在后面的源码分析中会发现,在 recordBatch 的迭代器逻辑中,直接采用的 compressionType 的解压逻辑对消息字节流读取的。也就是说,如果 recordBatch 是压缩的 ,只要对其进行了迭代访问,则会自动触发解压逻辑。

通俗一点讲:producer端配置了压缩,consumer自动解压缩

相关推荐
.生产的驴2 分钟前
SpringBoot 消息队列RabbitMQ在代码中声明 交换机 与 队列使用注解创建
java·spring boot·分布式·servlet·kafka·rabbitmq·java-rabbitmq
RestCloud11 分钟前
一文说清楚ETL与Kafka如何实现集成
数据仓库·kafka·etl·数据处理·数据集成
码爸1 小时前
flink自定义process,使用状态求历史总和(scala)
大数据·elasticsearch·flink·kafka·scala
happycao12312 小时前
记一次kafka消息丢失问题排查
kafka
喜欢猪猪12 小时前
Kafka是如何保证数据的安全性、可靠性和分区的
分布式·kafka
芊言芊语12 小时前
分布式消息服务Kafka版的详细解析和配置方式
分布式·kafka
方圆师兄20 小时前
docker快速搭建kafka
docker·容器·kafka
码爸21 小时前
flink kafka sink (scala)
flink·kafka·scala
圣圣不爱学习1 天前
阿里云kafka消息写入topic失败
阿里云·kafka
丁总学Java1 天前
maxwell 输出消息到 kafka
分布式·kafka·maxwell