Kafka 的高性能设计可以说是全方位的,从 Prodcuer 、到 Broker、再到 Consumer,
文章目录
1、如何理解高性能设计2、Kafka 高性能设计的全景图3、生产消息的性能优化手段
3.1 批量发送3.2 消息压缩3.3 高效序列化3.4 内存池复用 1、如何理解高性能设计
对于线程池、多级缓存、IO 多路复用、零拷贝等技术是一个系统性的问题,至少需要深入到操作系统层面。从 CPU 和存储入手,去了解底层的实现机制,然后再自底往上,一层一层去解密和贯穿起来。
高性能设计离不开的就是计算和IO
计算:
1、让更多的核来参与计算:比如用多线程代替单线程、用集群代替单机等。
2、减少计算量:比如用索引来取代全局扫描、用同步代替异步、通过限流来减少请求处理量、采用更高效的数据结构和算法等。
IO:
Linux 系统的 IO 栈
应用程序、操作系统、磁盘等各个层次来考虑性能优化
1.加快IO速度: 比如用磁盘顺序写代替随机写、用 NIO 代替 BIO、用性能更好的 SSD 代替机械硬盘等。2.减少IO 次数: 比如借助系统缓存或者外部缓存、通过零拷贝技术减少 IO 复制次数、批量读写、数据压缩等。 2、Kafka 高性能设计的全景图
消息队列本质就是
发 - 存 - 消费
生产消息、存储消息、消费消息:
Kafka 作为一个消息队列,很显然是一个 IO 密集型应用,它所面临的挑战除了磁盘 IO(Broker 端需要对消息持久化),还有网络 IO(Producer 到 Broker,Broker 到 Consumer,都需要通过网络进行消息传输)。
磁盘顺序 IO 的速度其实非常快,不亚于内存随机读写。这样网络 IO 便成为了 Kafka 的性能瓶颈所在。
Kafka 采用了批量发送消息的方式,通过将多条消息按照分区进行分组,然后每次发送一个消息集合,从而大大减少了网络传输的 overhead。
3.2 消息压缩消息压缩的目的是为了进一步减少网络传输带宽。而对于压缩算法来说,通常是:数据量越大,压缩效果才会越好。
因为有了批量发送这个前期,从而使得 Kafka 的消息压缩机制能真正发挥出它的威力(压缩的本质取决于多消息的重复性)。对比压缩单条消息,同时对多条消息进行压缩,能大幅减少数据量,从而更大程度提高网络传输率。
kafka支持3中压缩方式: gzip, snappy, lz4
gzip 压缩效果最好,但是生成耗时更长,综合对比 lz4 性能最佳。
压缩消息不仅仅减少了网络 IO,它还大大降低了磁盘 IO。因为批量消息在持久化到Brocker中的磁盘时,保存的是压缩状态, 最终在Consumer段解压缩, 这种端到端的压缩设计, 大大提高了写磁盘的效率。
3.3 高效序列化Kafka 消息中的 Key 和 Value,都支持自定义类型,只需要提供相应的序列化和反序列化器即可。因此,用户可以根据实际情况选用快速且紧凑的序列化方式(比如 ProtoBuf、Avro)来减少实际的网络传输量以及磁盘存储量,进一步提高吞吐量。
3.4 内存池复用前面说过 Producer 发送消息是批量的,因此消息都会先写入 Producer 的内存中进行缓冲,直到多条消息组成了一个 Batch,才会通过网络把 Batch 发给 Broker。
当这个 Batch 发送完毕后,显然这部分数据还会在 Producer 端的 JVM 内存中,由于不存在引用了,它是可以被 JVM 回收掉的。
JVM GC 时一定会存在 Stop The World 的过程,即使采用最先进的垃圾回收器,也势必会导致工作线程的短暂停顿,这对于 Kafka 这种高并发场景肯定会带来性能上的影响。
Kafka使用内存池机制, 和连接池, 线程池一样, 都是为了提高复用, 减少频繁的创建和释放
Producer 一上来就会占用一个固定大小的内存块,比如 64MB,然后将 64 MB 划分成 M 个小内存块(比如一个小内存块大小是 16KB)。
当需要创建一个新的 Batch 时,直接从内存池中取出一个 16 KB 的内存块即可,然后往里面不断写入消息,但最大写入量就是 16 KB,接着将 Batch 发送给 Broker ,此时该内存块就可以还回到缓冲池中继续复用了,根本不涉及垃圾回收。最终整个流程如下图所示: