Kafka(Go)教程(八)---生产者压缩算法详解及源码分析

用Python和CasADi实现高斯伪谱法:一个轨迹优化问题的保姆级教程 本文详细介绍了如何使用Python和CasADi框架实现高斯伪谱法解决轨迹优化问题。通过保姆级教程,从环境准备、问题建模到高斯伪谱法的核心实现,包括配点与权重计算、微分矩阵构造和动力学约束实现,最终完成问题求解与结果可视化。适用于机器人轨迹规划和无人机路径优化等工程场景。 阅读详情

来自:指月 https://www.lixueduan.com

原文:https://www.lixueduan.com/post/kafka/08-compression/

本文主要分析了 Kafka 生产者压缩与解压逻辑以及注事事项点,最终通过 Go 客户端 sarama 演示了 压缩算法的具体使用,并通过源码分析了 Go 客户端 sarama 的具体实现。

Kakfa 相关代码见 Github

1. 概述

压缩(compression)它秉承了用时间去换空间的经典 trade-off 思想,具体来说就是用 CPU 时间去换磁盘空间或网络 I/O 传输量,希望以较小的 CPU 开销带来更少的磁盘占用或更少的网络 I/O 传输。

2. 具体流程

一句话概括为:Producer 端压缩、Broker 端保持、Consumer 端解压缩。

2.1 压缩

在 Kafka 中,压缩可能发生在两个地方:Producer 端和 Broker 端。

Producer 程序中配置 compression.type 参数即表示启用指定类型的压缩算法。比如下面这段程序代码展示了如何构建一个开启 GZIP 的 Producer 对象:

 Properties props = new Properties();
 props.put("bootstrap.servers", "localhost:9092");
 props.put("acks", "all");
 props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
 props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
 // 开启GZIP压缩
 props.put("compression.type", "gzip");
 
 Producer<String, String> producer = new KafkaProducer<>(props);

这里比较关键的代码行是 props.put(“compression.type”, “gzip”),它表明该 Producer 的压缩算法使用的是 GZIP。这样 Producer 启动后生产的每个消息集合都是经 GZIP 压缩过的,故而能很好地节省网络传输带宽以及 Kafka Broker 端的磁盘占用。

除了在 Producer 端进行压缩外有两种例外情况就可能让 Broker 重新压缩消息:

1)Broker 端指定了和 Producer 端不同的压缩算法。

比如 Producer 使用 GZIP,而 Broker 只能接收使用 Snappy 算法压缩的消息。这种情况下 Broker 接收到 GZIP 压缩消息后,只能解压缩然后使用 Snappy 重新压缩一遍。

Broker 端也有一个参数叫 compression.type ,默认情况下为 Producer,表示 Broker 端会“尊重” Producer 端使用的压缩算法。

如果设置了不同的 compression.type 值,可能会发生预料之外的压缩 / 解压缩操作,通常表现为 Broker 端 CPU 使用率飙升。

2)Broker 端发生了消息格式转换。

Kafka 的消息层次分为两层:消息集合(message set)以及消息(message)。一个消息集合中包含若干条日志项(record item),而日志项才是真正封装消息的地方。Kafka 底层的消息日志由一系列消息集合日志项组成。Kafka 通常不会直接操作具体的一条条消息,它总是在消息集合这个层面上进行写入操作。

目前 Kafka 共有两大类消息格式,社区分别称之为 V1 版本和 V2 版本。V2 版本是 Kafka 0.11.0.0 中正式引入的。

V2 版本优化点:

  • 1)在 V1 版本中,每条消息都需要执行 CRC 校验,比较浪费 CPU ,在 V2 版本中,消息的 CRC 校验工作就被移到了消息集合这一层。

  • 2)V1 版本中保存压缩消息的方法是把多条消息进行压缩然后保存到外层消息的消息体字段中;而 V2 版本的做法是对整个消息集合进行压缩。显然后者应该比前者有更好的压缩效果。

在一个生产环境中,Kafka 集群中同时保存多种版本的消息格式非常常见。为了兼容老版本的格式,Broker 端会对新版本消息执行向老版本格式的转换。这个过程中会涉及消息的解压缩和重新压缩。一般情况下这种消息格式转换对性能是有很大影响的,除了这里的压缩之外,它还让 Kafka 丧失了引以为豪的 Zero Copy 特性。

Zero Copy 的前提就是应用程序不需要对数据进行处理。

2.2 解压

通常来说解压缩发生在 Consumer 程序中,也就是说 Producer 发送压缩消息到 Broker 后,Broker 照单全收并原样保存起来。当 Consumer 程序请求这部分消息时,Broker 依然原样发送出去,当消息到达 Consumer 端后,由 Consumer 自行解压缩还原成之前的消息。

为了让 Consumer 知道使用哪种压缩算法,Kafka 会将启用了哪种压缩算法封装进消息集合中,这样当 Consumer 读取到消息集合时,它自然就知道了这些消息使用的是哪种压缩算法。

Broker 端也会进行解压缩。每个压缩过的消息集合在 Broker 端写入时都要发生解压缩操作,目的就是为了对消息执行各种验证。而且这种校验是非常重要的,也不能直接去掉。

这种解压缩对 Broker 端性能是有一定影响的,特别是对 CPU 的使用率而言。

3. 压缩算法

先比较一下各个压缩算法的优劣,这样我们才能有针对性地配置适合我们业务的压缩策略。

看一个压缩算法的优劣,有两个重要的指标:一个指标是压缩比,另一个指标就是压缩 / 解压缩吞吐量。

下面这张表是 Facebook Zstandard提供的一份压缩算法 benchmark 比较结果:

Compressor nameRatioCompressionDecompress.
zstd 1.4.5 -12.884500 MB/s1660 MB/s
zlib 1.2.11 -12.74390 MB/s400 MB/s
brotli 1.0.7 -02.703400 MB/s450 MB/s
zstd 1.4.5 --fast=12.434570 MB/s2200 MB/s
zstd 1.4.5 --fast=32.312640 MB/s2300 MB/s
quicklz 1.5.0 -12.238560 MB/s710 MB/s
zstd 1.4.5 --fast=52.178700 MB/s2420 MB/s
lzo1x 2.10 -12.106690 MB/s820 MB/s
lz4 1.9.22.101740 MB/s4530 MB/s
zstd 1.4.5 --fast=72.096750 MB/s2480 MB/s
lzf 3.6 -12.077410 MB/s860 MB/s
snappy 1.1.82.073560 MB/s1790 MB/s

从表中我们可以发现 zstd 算法有着最高的压缩比,而在吞吐量上的表现只能说中规中矩。反观 LZ4 算法,它在吞吐量方面则是毫无疑问的执牛耳者。

在实际使用中,GZIP、Snappy、LZ4 甚至是 zstd 的表现各有千秋。但对于 Kafka 而言,它们的性能测试结果却出奇得一致,即:

  • 在吞吐量方面:LZ4 > Snappy > zstd 和 GZIP;

  • 而在压缩比方面,zstd > LZ4 > GZIP > Snappy。

  • 具体到物理资源,使用 Snappy 算法占用的网络带宽最多,zstd 最少,这是合理的,毕竟 zstd 就是要提供超高的压缩比;

  • 在 CPU 使用率方面,各个算法表现得差不多,只是在压缩时 Snappy 算法使用的 CPU 较多一些,而在解压缩时 GZIP 算法则可能使用更多的 CPU。

4. Go 压缩实战

不同语言客户端的 解压和压缩可能会有差异,但是大致流程应该不会变化。

4.1 Demo

Go 里面使用压缩也是在 Producer 端通过 config 进行配置,相关配置如下:

type Config struct{
    Producer struct {
        Compression CompressionCodec
		CompressionLevel int
    }
}
type CompressionCodec int8

通过Config.Producer.Compression指定压缩算法,通过Config.Producer.CompressionLevel指定压缩等级。

压缩算法是通过枚举值指定的,内置算法列表如下:

const (
	// CompressionNone no compression
	CompressionNone CompressionCodec = iota
	// CompressionGZIP compression using GZIP
	CompressionGZIP
	// CompressionSnappy compression using snappy
	CompressionSnappy
	// CompressionLZ4 compression using LZ4
	CompressionLZ4
	// CompressionZSTD compression using ZSTD
	CompressionZSTD
)

具体使用哪种算法还要根据 Kafka 版本决定,不同 Kafka 版本的这些算法的支持不一样。

完整代码如下:

var defaultMsg = strings.Repeat("Golang", 1000)

func Producer(topic string, limit int) {
	config := sarama.NewConfig()
	// config.Producer.Compression = sarama.CompressionGZIP
	// config.Producer.CompressionLevel = gzip.BestCompression
	config.Producer.Return.Successes = true
	config.Producer.Return.Errors = true // 这个默认值就是 true 可以不用手动 赋值

	producer, err := sarama.NewSyncProducer([]string{kafka.HOST}, config)
	if err != nil {
		log.Fatal("NewSyncProducer err:", err)
	}
	defer producer.Close()
	for i := 0; i < limit; i++ {
		msg := &sarama.ProducerMessage{Topic: topic, Key: nil, Value: sarama.StringEncoder(defaultMsg)}
		partition, offset, err := producer.SendMessage(msg)
		if err != nil {
			log.Println("SendMessage err: ", err)
			return
		}
		log.Printf("[Producer] partitionid: %d; offset:%d\n", partition, offset)
	}
}

发送了 1000 条消息测试一下压缩效果:

每条消息为 6000 byte,1000 条差不多 6M

压缩前:

I have no name!@cd04519bf1df:/bitnami/kafka/data/compression-0$ ls -lhS
total 6.0M
-rw-r--r-- 1 1001 root  10M Aug 22 02:32 00000000000000000000.index
-rw-r--r-- 1 1001 root  10M Aug 22 02:32 00000000000000000000.timeindex
-rw-r--r-- 1 1001 root 6.0M Aug 22 02:32 00000000000000000000.log
-rw-r--r-- 1 1001 root    8 Aug 22 02:28 leader-epoch-checkpoint

果然是 6 M 和预期一致。

压缩后:

I have no name!@cd04519bf1df:/bitnami/kafka/data/compression-0$ ls -lhS
total 140K
-rw-r--r-- 1 1001 root  10M Aug 22 02:34 00000000000000000000.index
-rw-r--r-- 1 1001 root  10M Aug 22 02:34 00000000000000000000.timeindex
-rw-r--r-- 1 1001 root 125K Aug 22 02:34 00000000000000000000.log
-rw-r--r-- 1 1001 root    8 Aug 22 02:33 leader-epoch-checkpoint

125 K,说明压缩是生效的。

这么高的压缩比是因为发送的内容全是重复字符串导致的,使用真实数据应该没这么高压缩比。

4.2 源码分析

在之前文章Kafka(Go)教程(六)—sarama 客户端 producer 源码分析 中分析了 Producer 的具体流程,其中消息会经过 TopicProducer、PartitionProducer 最终通过 BrokerProudcer 到达 Kafka。

由于压缩是发生在 Producer 的,所以肯定是在最终通过 BrokerProudcer 到达 Kafka 之前就需要进行压缩,有了大致方向于是开始翻源码。

最终在 BrokerProudcer 的 buildRequest 方法中找到了答案:

为了便于阅读,省略了无关代码

buildRequest

在发生到 Kafka 之前对消息进行一次格式化。

// produce_set.go 132 行
func (ps *produceSet) buildRequest() *ProduceRequest {
	req := &ProduceRequest{
		RequiredAcks: ps.parent.conf.Producer.RequiredAcks,
		Timeout:      int32(ps.parent.conf.Producer.Timeout / time.Millisecond),
	}

	for topic, partitionSets := range ps.msgs {
		for partition, set := range partitionSets {
			if ps.parent.conf.Producer.Compression == CompressionNone {
				req.AddSet(topic, partition, set.recordsToSend.MsgSet)
			} else {
				payload, err := encode(set.recordsToSend.MsgSet, ps.parent.conf.MetricRegistry)
				if err != nil {
					Logger.Println(err) // if this happens, it's basically our fault.
					panic(err)
				}
				compMsg := &Message{
					Codec:            ps.parent.conf.Producer.Compression,
					CompressionLevel: ps.parent.conf.Producer.CompressionLevel,
					Key:              nil,
					Value:            payload,
					Set:              set.recordsToSend.MsgSet, // Provide the underlying message set for accurate metrics
				}
			}
		}
	}

	return req
}

核心逻辑如下:

if ps.parent.conf.Producer.Compression == CompressionNone {
	req.AddSet(topic, partition, set.recordsToSend.MsgSet)
} else {
	payload, err := encode(set.recordsToSend.MsgSet, ps.parent.conf.MetricRegistry)
}

前面提到过是按照 消息集合 为单位进行压缩的,这里的 MsgSet 就是那个消息集合。

然后为了 Consumer 能知道如何解压,把压缩算法和压缩等级信息附加在了消息上:

compMsg := &Message{
    Codec:            ps.parent.conf.Producer.Compression,
    CompressionLevel: ps.parent.conf.Producer.CompressionLevel,
    Key:              nil,
    Value:            payload,
    Set:              set.recordsToSend.MsgSet, // Provide the underlying message set for accurate metrics
}

如果不需要压缩则直接添加到 Set,如果否则就调用encode方法,合理猜测,压缩逻辑就在 encode 里面,继续追踪下去:

encode
// encoder_decoder.go 21 行
func encode(e encoder, metricRegistry metrics.Registry) ([]byte, error) {
	var prepEnc prepEncoder
	var realEnc realEncoder

	err := e.encode(&prepEnc)
	if err != nil {
		return nil, err
	}

	if prepEnc.length < 0 || prepEnc.length > int(MaxRequestSize) {
		return nil, PacketEncodingError{fmt.Sprintf("invalid request size (%d)", prepEnc.length)}
	}

	realEnc.raw = make([]byte, prepEnc.length)
	realEnc.registry = metricRegistry
	err = e.encode(&realEnc)
	if err != nil {
		return nil, err
	}

	return realEnc.raw, nil
}

可以看到这里面有prepEncoder和realEncoder 两个 encoder,其中 prepEncoder 只是走了一遍 encode 流程并没有真执行 encode:

type prepEncoder struct {
	stack  []pushEncoder
	length int
}

// primitives

func (pe *prepEncoder) putInt8(in int8) {
	pe.length++
}

func (pe *prepEncoder) putInt16(in int16) {
	pe.length += 2
}

func (pe *prepEncoder) putInt32(in int32) {
	pe.length += 4
}

func (pe *prepEncoder) putInt64(in int64) {
	pe.length += 8
}

主要是为了获取 length 进行下面这个校验:

	if prepEnc.length < 0 || prepEnc.length > int(MaxRequestSize) {
		return nil, PacketEncodingError{fmt.Sprintf("invalid request size (%d)", prepEnc.length)}
	}

所以真正逻辑肯定在 realEncoder 里,继续跟进。

最后发现全是 interface 不是很好追踪,对于这种情况就需要面向实现跟进,比如之前是 MsgSet 调用的 encode 方法,这里就直接找到 MsgSet 的相关实现。

type MessageSet struct {
	PartialTrailingMessage bool // whether the set on the wire contained an incomplete trailing MessageBlock
	OverflowMessage        bool // whether the set on the wire contained an overflow message
	Messages               []*MessageBlock
}

func (ms *MessageSet) encode(pe packetEncoder) error {
	for i := range ms.Messages {
		err := ms.Messages[i].encode(pe)
		if err != nil {
			return err
		}
	}
	return nil
}

又调用了ms.Messages[i].encode(pe),Messages 切片里存的是 MessageBlock,所以继续跟进 MessageBlock:

type MessageBlock struct {
	Offset int64
	Msg    *Message
}

func (msb *MessageBlock) encode(pe packetEncoder) error {
	pe.putInt64(msb.Offset)
	pe.push(&lengthField{})
	err := msb.Msg.encode(pe)
	if err != nil {
		return err
	}
	return pe.pop()
}

又调用了msb.Msg.encode(pe),继续跟进 Message:

func (m *Message) encode(pe packetEncoder) error {

	var payload []byte
	payload, err = compress(m.Codec, m.CompressionLevel, m.Value)
    if err != nil {
        return err
    }
    m.compressedCache = payload
    // Keep in mind the compressed payload size for metric gathering
    m.compressedSize = len(payload)

	if err = pe.putBytes(payload); err != nil {
		return err
	}

	return pe.pop()
}
compress

终于找到了compress(m.Codec, m.CompressionLevel, m.Value),具体实现:

func compress(cc CompressionCodec, level int, data []byte) ([]byte, error) {
	switch cc {
	case CompressionNone:
		return data, nil
	case CompressionGZIP:
		var (
			err    error
			buf    bytes.Buffer
			writer *gzip.Writer
		)

		switch level {
		case CompressionLevelDefault:
			writer = gzipWriterPool.Get().(*gzip.Writer)
			defer gzipWriterPool.Put(writer)
			writer.Reset(&buf)
		case 1:
			writer = gzipWriterPoolForCompressionLevel1.Get().(*gzip.Writer)
			defer gzipWriterPoolForCompressionLevel1.Put(writer)
			writer.Reset(&buf)
		case 2:
			writer = gzipWriterPoolForCompressionLevel2.Get().(*gzip.Writer)
			defer gzipWriterPoolForCompressionLevel2.Put(writer)
			writer.Reset(&buf)
		case 3:
			writer = gzipWriterPoolForCompressionLevel3.Get().(*gzip.Writer)
			defer gzipWriterPoolForCompressionLevel3.Put(writer)
			writer.Reset(&buf)
		case 4:
			writer = gzipWriterPoolForCompressionLevel4.Get().(*gzip.Writer)
			defer gzipWriterPoolForCompressionLevel4.Put(writer)
			writer.Reset(&buf)
		case 5:
			writer = gzipWriterPoolForCompressionLevel5.Get().(*gzip.Writer)
			defer gzipWriterPoolForCompressionLevel5.Put(writer)
			writer.Reset(&buf)
		case 6:
			writer = gzipWriterPoolForCompressionLevel6.Get().(*gzip.Writer)
			defer gzipWriterPoolForCompressionLevel6.Put(writer)
			writer.Reset(&buf)
		case 7:
			writer = gzipWriterPoolForCompressionLevel7.Get().(*gzip.Writer)
			defer gzipWriterPoolForCompressionLevel7.Put(writer)
			writer.Reset(&buf)
		case 8:
			writer = gzipWriterPoolForCompressionLevel8.Get().(*gzip.Writer)
			defer gzipWriterPoolForCompressionLevel8.Put(writer)
			writer.Reset(&buf)
		case 9:
			writer = gzipWriterPoolForCompressionLevel9.Get().(*gzip.Writer)
			defer gzipWriterPoolForCompressionLevel9.Put(writer)
			writer.Reset(&buf)
		default:
			writer, err = gzip.NewWriterLevel(&buf, level)
			if err != nil {
				return nil, err
			}
		}
		if _, err := writer.Write(data); err != nil {
			return nil, err
		}
		if err := writer.Close(); err != nil {
			return nil, err
		}
		return buf.Bytes(), nil
	case CompressionSnappy:
		return snappy.Encode(data), nil
	case CompressionLZ4:
		writer := lz4WriterPool.Get().(*lz4.Writer)
		defer lz4WriterPool.Put(writer)

		var buf bytes.Buffer
		writer.Reset(&buf)

		if _, err := writer.Write(data); err != nil {
			return nil, err
		}
		if err := writer.Close(); err != nil {
			return nil, err
		}
		return buf.Bytes(), nil
	case CompressionZSTD:
		return zstdCompress(nil, data)
	default:
		return nil, PacketEncodingError{fmt.Sprintf("unsupported compression codec (%d)", cc)}
	}
}

就是一个简单的 switch 语句,根据指定的压缩算法和压缩等级选择具体实现,可以看到 压缩等级只有 GZIP 才需要设置。

gzip 这里的实现感觉有点辣眼睛,虽然使用了 sync.pool 防止频繁创建 writer 对象是一个优化手段,但是因为压缩等级不同所以为每个压缩等级都创建了一个 pool 感觉不够优雅,可能是这样性能上会好一些,毕竟少一层封装。

到此 sarama 的压缩逻辑已经分析完了,还是比较简单的。其中用到了 sync.pool 临时对象池,进行 writer 对象复用避免了创建大量临时对象,以提升性能。

consumer 解压也是差不多的逻辑这里就不分析了。

作者能力实在是有限,文中很有可能会有一些错误的理解。所以当你发现了一些违和的地方,也请不吝指教,谢谢你!

再次感谢你能看到这里!

4.3 小结

sarama 实现和官方逻辑大体一致:

  • 1)producer 中配置压缩算法和压缩等级
  • 2)发送消息前按照 MsgSet 为单位进行压缩,并将 压缩算法和压缩等级 附加到消息中便于 consumer 解压。
  • 3)consumer 根据消息中附加的 压缩算法和压缩等级进行解压

5. 小结

Kakfa 相关代码见 Github

具体流程:

Producer 端压缩、Broker 端保持、Consumer 端解压缩。

特殊情况:

  • 1)Broker 端指定了和 Producer 端不同的压缩算法。
  • 2)Broker 端发生了消息格式转换。

以上两种情况 Broker 端都需要解压后在重新压缩。除此之外 Broker 端为了进行消息校验,也需要进行一次解压。

压缩算法:

  • 在吞吐量方面:LZ4 > Snappy > zstd 和 GZIP;
  • 而在压缩比方面,zstd > LZ4 > GZIP > Snappy。

使用建议:

由于压缩&解压需要消耗 CPU,所以建议在以下情况才开启:

  • 1)Producer 、Consumer CPU 充足
  • 2)带宽资源有限

如果 CPU 资源有很多富余,而带宽受限,这种情况强烈建议开启 zstd 压缩,这样能极大地节省网络资源消耗。

Go 优化:

使用 sync.pool 复用对象,避免创建大量临时对象。

6. 参考

https://github.com/Shopify/sarama

《Kafka 核心技术与实战》

https://github.com/facebook/zstd/

MAX96712在车载摄像头系统中的带宽分配与传输能力计算解析 在计算摄像头带宽与传输接口的关系时,需要考虑视频流的像素分辨率、帧率、颜色深度等因素。以 MAX96712 这种 GMSL2 产品为例,它支持每个链路在正向方向以 3Gbps 或 6Gbps 的固定速率工作。 阅读详情

相关推荐

Canoe Panel基础功能介绍

CANoe Panel 是 Vector CANoe 软件中用于设计图形化操作界面的功能模块。用户可以在面板上添加合适的控件,实时的改变信号或者变量的值

m0_56208280的博客 1951

Kafka知识点

RabbitMQ和Kafka是两个常用的分布式消息队列系统,它们的主要区别包括以下几个方面:架构模式:RabbitMQ采用模式和模式,即消息生产者将消息发送到特定的队列,然后等待消费者来处理这个消息,或者发送到订阅的exchange,由绑定到此exchange的队列接收;而Kafka采用模式,即消息生产者把消息发送给主题(topic),然后订阅这个主题的所有消费者都可以接收到消息。

weixin_42094245的博客 293

六自由度机器人(机械臂)运动学建模及运动规划系列(一)——简介

毕业设计做了六轴机器人相关的课题,做完之后学到很多,在这里分享一下。

weixin_47849087的博客 2万+

Kafka精进 | Producer端核心参数及调优建议

前言在前面文章《Kafka精进 | 一文读懂Producer消息发送机制》中,我们从Kafka消息结构、序列化器、分区器及消息缓冲池等方面介绍了Producer端的原理,回顾示意图如下:...

Focus on Lakehouse 567

DBeaver连接IoTDB避坑指南:手动配置JDBC驱动的完整流程(附问题排查)

本文提供了在DBeaver中手动配置Apache IoTDB JDBC驱动的完整流程与避坑指南。详细解析了从驱动创建、库文件添加(关键是指向整个lib目录)到连接测试的每一步,并重点针对常见的ClassNotFoundException、连接拒绝等错误提供了深度排查方案,帮助用户高效建立图形化管理连接。

weixin_28327051的博客 396

从仿真到流片:手把手教你优化65nm CMOS电荷泵的电流匹配与稳定性

本文详细介绍了在65nm CMOS工艺下优化电荷泵电流匹配与稳定性的工程实践指南。通过蒙特卡洛仿真、运放反馈结构版图技巧和全差分结构设计,实现优于1%的电流匹配精度和±2%以内的电流稳定性,特别针对current mismatch问题提供了有效的解决方案。

weixin_42522320的博客 417

Kafka消息的压缩机制

大纲Kafka支持的消息压缩类型什么是 Kafka 的消息压缩消息压缩类型何时需要压缩如何开启压缩在 Broker 端开启压缩compression.type 属性broker 和 topic 两个级别broker 级别topic 级别在 Producer 端压缩compression.type 属性开启压缩的方式压缩和解压的位置何处会压缩producer 端broker 端何处会解压consumer 端broker 端压缩和解压原理CompressionTypeCompressionCodecCompre

sherlockyb的博客 6603

AI应用安全 - Prompt注入攻击

随着大模型的爆火,各行各业都在使用大模型进行智能化改造。但是如何做一个可靠的大模型应用,就需要考虑各种安全问题。本文主要介绍一种常见的安全问题,即prompt注入攻击,并介绍一些防范手段。Prompt Injection Attack(提示注入攻击)是针对大型语言模型(LLM)的一种新兴安全威胁,攻击者通过精心构造的输入提示(prompt)来操纵模型行为,使其产生非预期输出或执行恶意操作。它主要包括两种注入方式,一是直接注入,而是间接注入。

sinat_28694519的博客 1932

Kafka参数调优

1.broker Kafka性能调优 kafka性能调优 2. producer buffer.memory:33554432 (32m) #在Producer端用来存放尚未发送出去的Message的缓冲区大小。缓冲区满了之后可以选择阻塞发送或抛出异常,由block.on.buffer.full的配置来决定。 compression.type:none #默认发送不进行压缩,推荐配置一种适合的压缩算法,可以大幅度的减缓网络压力和Broker的存储压力。 linger.ms:0 #Producer

紫蝶侠的博客 6005

IoU的进化之路——IoU、GIoU、DIoU、CIoU四种损失函数总结

IoU、GIoU、DIoU、CIoU四种损失函数总结

chase 8131

YOLOv11数据集增强 | 引入多种数据集增强方法,支持图像和标签同步扩充增强

本文介绍了YOLOv11目标检测中的数据集增强技术,重点解决图像与标注标签同步扩充的关键问题。文章系统分析了三类增强方法:几何变换(翻转、旋转、缩放)、颜色变换(亮度、对比度调整)和高级增强(Mosaic、MixUp),并探讨了其在小样本检测、复杂场景泛化和多方向目标识别等场景的应用价值。针对核心的标签同步问题,提供了基于Python和OpenCV的详细代码实现示例,展示如何对图像进行水平翻转和缩放操作时精确调整YOLO格式边界框坐标。该技术能有效解决训练数据不足和多样性匮乏问题,显著提升模型的泛化性能。

走向CTO的路上... 1519

Kafka配置文件详解

Kafka配置文件详解

jishuaitao的博客 1749

PGM-Explainer实验总结

PGM-Explainer建立了一个概率图模型,为GNN提供实例级解释。 论文标题:PGM-Explainer: Probabilistic Graphical Model Explanations for Graph Neural Networks 论文地址:2010.05788.pdf (arxiv.org) PGM-Explainer实验 实验目的 研究PGM-Explainer的有效性 实验方案 合成数据集的节点分类 与其他解释器做对比实验 使用六个合成数据集。 每个输入图都是一个

笔记|思路|总结 1万+

Sarama终极指南:从零开始掌握Apache Kafka Go客户端的完整学习路径

Sarama是一个MIT许可的Go客户端库,专为Apache Kafka设计,提供了全面的Kafka协议支持和丰富的功能特性。本指南将帮助你从零开始,快速掌握这个强大工具的使用方法,轻松构建高性能的Kafka应用程序。 ## 📚 什么是Sarama? Sarama为Go开发者提供了与Apache Kafka集群交互的完整解决方案。作为一个成熟的客户端库,它支持 Kafka 的各种高级特性,包

gitblog_00561的博客 352

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

kafka-go消息压缩全解析:Snappy、Gzip与LZ4性能对比

在Kafka消息传递中,压缩机制是提升吞吐量和降低网络带宽消耗的关键技术。本文将深入解析[kafka-go](https://link.gitcode.com/i/c5b7ce450770bec29d700c77f27d5344)库支持的Snappy、Gzip与LZ4三种压缩算法,通过源码分析和性能对比,帮助开发者选择最适合业务场景的压缩方案。 ## 压缩框架设计 [kafka-go](htt...

gitblog_00588的博客 837

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

Go岗位基本要点

【语法基础-中间件原理-源码分析-项目实战-云原生】 一:基础 语法基础:错误处理,包定义以及导入,结构体定义,反射原理,闭包,值传递,引用传递,defer函数。 并发编程:goroutine,锁(读写锁,互斥锁,原子操作),通道channel,runtime包解析,Context使用原则。 网络编程:tcp编程,udp编程,http的实现,websocket。 第三方测试框架:goconvey,gostub,gomock,monkey。 源码分析:GC实现,调度器源码,定时器实现,map与切片 二:中间件

rhelredhat的博客 336

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

kafka问题总结与优化

​​​​​​​​​​​​​​ 总结:kafka 第一,怎么解决kafka日志文件占磁盘空间过大的问题 1)配置文件:producer.properties 参数: compression.codec://是否压缩,0代表不压缩,1代表用gzip压缩,2代表用snappy压缩 compressed.topics://如果要...

qq_36250202的博客 1174

kafka学习三:设计

在开始开发producer和consumer之前,先从设计的角度看一看Kafka。 由于重度依赖JMS,且实现方式各异、对可伸缩架构的支持不够,LinkedIn开发了Kafka来实现对活动流数据和运营指标数据的监控,这些数据包括CPU、I/O使用数据、请求响应时间等。开发Kafka时主要目标是提供以下特性: 支持自定义实现的producer和consumer的API以低开销的网络和存储实现

paul342的专栏 1533

Kafka的消息格式是怎样的?Kafka消息的压缩机制有哪些?Kafka的高可用性如何保证?Kafka的消息传递模式有哪些?

Kafka的消息格式并不限制消息的内容,开发人员可以自由定义消息的格式和结构。点对点模式:在点对点模式下,消息被发送到一个特定的消费者,通常是基于消息的目的地(例如队列或主题)。综上所述,Kafka通过分布式架构、副本机制、ISR机制、自动故障恢复以及监控和报警等多种机制来保证高可用性,从而能够在节点故障或网络故障的情况下继续提供可靠的消息传递服务。Zstandard压缩:Zstandard是一种新型的压缩算法,它在保持较高压缩率的同时,提供了比其他算法更快的压缩和解压缩速度。

luoluoal的博客 1564

Golang学习篇—kafka客户端压缩

1.kafka压缩参数说明 compression.type: producer用于压缩数据的压缩类型。默认是无压缩。正确的选项值是none、gzip、snappy和lz4。压缩最好用于批量处理,批量处理消息越多,压缩性能越好。推荐配置一种适合的压缩算法,可以大幅度的减缓网络压力和Broker的存储压力。 kafka的压缩和kafka的compact是不同的,compact就是相同的key...

Map的博客 1568

Kafka ProducerConfig和ConsumerConfig配置

20.max.in.flight.requests.per.connection可以在一个connection中发送多个请求,叫作一个flight,这样可以减少开销,但是如果产生错误,可能会造成数据的发送顺序改变,默认5。13.fetch.max.wait.ms从Kafka拉取消息时,在不满足fetch.min.bytes条件时,等待的最大时间,默认500ms。11.reconnect.backoff.max.ms尝试连接到Kafka,生产者客户端等待的最大时间,默认1000ms。...

程序员一博 3941

(一)kafka实战——kafka源码编译启动

本节内容是关于kafka消息中间键的源码编译,并通过idea工具实现kafka服务器的启动,使用的kafka源码版本是3.6.1,由于kafka源码是通过gradle编译的,以及服务器是通过scala语言实现,我们要预先安装好gradle编译工具和scala环境,同时需要安装scala依赖的java环境jdk17。

厉害哥哥的博客 3554

【kafka】简单运用go语言操作kafka实现生产者和消费者功能的包,confluent-kafka-go和sarama

提供了 Kafka 的核心功能,但可能在一些高级特性上不如 confluent-kafka-go 丰富。例如,sarama 对事务支持相对较弱,尽管在常规的生产/消费场景中功能足够。虽然 sarama 的性能不及 confluent-kafka-go,但它对于大多数常规用途仍然足够快,特别是在吞吐量要求不是特别高的场景中。支持 Kafka 的基本功能,如生产者、消费者组、消息传递等,但对一些高级功能(如流控、集群管理等)的支持可能稍有不足。选择 sarama:适用于不需要复杂配置和高级特性的场景。

m0_74282926的博客 2379

Kafka基础—2、Kafka 生产者API

Kafka 生产者API:发送消息,消息压缩,生产者配置,分区器(Partitioner),序列化(Serializer)

风不归的乐园 1494

深度解析提升 Kafka 效率的多种关键手段

过大的 fetch.min.bytes 可能会导致消息延迟增加,过小的 fetch.min.bytes 则无法充分发挥批获取的优势。需要根据实际情况调整 Consumer 的配置参数,例如 fetch.min.bytes、fetch.max.wait.ms、auto.commit.interval.ms 等。需要根据消息的特点选择合适的压缩算法。在未来的工作中,我们将继续深入研究 Kafka 的性能优化问题,探索更加高效的消息处理方式和更加优化的系统架构,为 Kafka 的应用和发展提供更加有力的支持。

Sunshine博客 1381

sarama架构

1.client创建producer或consumer时,都会创建一个client与之关联,client负责获取metadata信息,并且client会启动一个backgroundMetadataUpdater,定期获取metadata。client维护一个broker表,client在创建时,就连接到kafka broker,并启动broker的responseReceiver,用于接收kafka

mdj67887500的专栏 6203
上一篇: Kafka(Go)教程(七)---生产者分区机制原理剖析及实战
下一篇: Kafka(Go)教程(九)---如何避免消息丢失?
探索云原生
博客等级 码龄9年 730粉丝 131原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值