美文网首页
Kafka性能测试及分析

Kafka性能测试及分析

作者: JackH_acb6 | 来源:发表于2019-03-08 10:44 被阅读0次

    Kafka 简介

    Kafka 是一种基于 Zookeeper 协调的分布式消息系统,使用 Scala 编写。其主要设计目标如下:

    • 以时间复杂度为 O(1) 的方式提供消息持久化能力,即使对 TB 级以上数据也能保证常数时间复杂度的访问性能。
    • 高吞吐率。即使在非常廉价的商用机器上也能做到单机支持每秒 100K 条以上消息的传输。
    • 支持 Kafka Server 间的消息分区,及分布式消费,同时保证每个 Partition 内的消息顺序传输。
    • 同时支持离线数据处理和实时数据处理。
    • Scale out:支持在线水平扩展。

    基础概念

    Broker

    Kafka 集群中每一台服务器被称为一个 Broker。

    Topic

    一类消息,每条消息发布到Kafka都会隶属于一个 Topic。

    Partition

    物理上把 Topic 分成一个或多个 Partition,每个 Partition 在物理上对应一个文件夹,文件夹名为 Topic 名称 + Partition Index(例如:test_topic-0),该文件夹下存储这个 Partition 的所有消息和索引文件,每个 Partition 里面存储的消息是有序的。

    Segment

    一个 Partition 物理上由多个 Segment 组成,每个 Partition 里面的消息会被分配到多个大小相等的 Segment 数据文件中,但由于消息的大小不等,所以每个 Segment 里面的消息总数不一定相等。

    Producer

    消息生产者,负责发布消息到 Kafka。

    Consumer

    消息消费者,向 Kafka 读取并消费消息。

    Consumer Group

    每个 Consumer 属于一个特定的 Consumer Group,每条消息只能被 Consumer Group 中的 Consumer 一个消费,但是可以被多个 Consumer Group 消费。

    Kafka的性能究竟有多好

    Kafka给人们影响最深刻的应该就是快了,出奇的快,据 LinkedIn 统计,最新的数据是每天利用 Kafka 处理的消息超过1万亿条,在峰值时每秒钟会发布超过百万条消息,就算是在内存和 CPU 都不高的情况下,Kafka 的速度最高可以达到每秒十万条数据,并且还能持久化存储。

    首先我们来验证一下这个数据是否属实,有没有水分,测试工具使用官方提供的 kafka-producer-perf-test.sh,测试单台服务器在发送5千万条大小为1KB的消息时性能如何。

    服务器

    CPU:2.3 GHz Intel Core i5

    内存:8 GB 2133 MHz LPDDR3

    硬盘:128GB PCIe SSD

    JDK:1.8

    Kafka主要配置

    Broker:1台

    num.network.threads(用于接收并处理网络请求的线程数):3

    num.io.threads(用于接收并处理磁盘IO的线程数 ):8

    num.partitions(每个 Topic 下面的 Partition 数量,直接影响 Kafka 集群的吞吐性能,理论上 Partition 越多吞吐量越大,但相应的延迟和内存开销越大,并且高可用性降低):3

    其他配置均使用默认值

    性能测试命令

    kafka-producer-perf-test --producer-props bootstrap.servers=localhost:9092 --topic Test --throughput 1000000 --record-size 1024 --num-records 50000000
    

    --producer-props bootstrap.servers:Kafka 的服务器地址及端口

    --topic:发送的消息所属类别

    --throughput:最大信息吞吐量,单位为 messages/sec,数值越小消息延迟越小

    --record-size:每条消息的大小,单位为 bytes

    --num-records:要发送的消息条数

    测试结果

    50000000 records sent, 143633.997593 records/sec (140.27 MB/sec), 213.37 ms avg latency, 574.00 ms max latency, 209 ms 50th, 269 ms 95th, 317 ms 99th, 449 ms 99.9th.
    
    性能测试

    在后台跑着许多程序的情况下,Kafka 依然跑出了平均每秒发送14万+条的吞吐量,可见 Kafka 的性能确实非常优秀。

    Kafka的性能为什么这么好

    1.顺序读写

    Kafka的消息是不断追加到文件中的,这个特性使它可以充分利用磁盘的顺序读写能力。顺序读写降低了硬盘磁头的寻道时间,只需要很少的扇区旋转时间,所以速度远快于随机读写。Kafka官方给出的测试数据(Raid-5, 7200rpm),顺序I/O: 600MB/s,随机I/O: 100KB/s。

    2.批量发送

    即使顺序读写,过于频繁的大量小I/O操作一样会造成磁盘的瓶颈,所以 Kafka 在此处的处理是先将消息缓存在内存中,然后一次请求批量发送出去,比如可以指定缓存的消息到某个量的时候发送,或者缓存了固定的时间后发送这种策略大大减少了服务器端的I/O次数。

    3.文件分段

    前文介绍了 Kafka 的队列 Topic 被分为了多个区 Partition,每个 Partition 又分为了多个 Segment,所以一个队列中的消息实际上是保存在N多个片段文件中,通过分段的方式,每次文件操作都是对一个小文件的操作,非常轻便,同时也增加了并行处理能力。

    4.零拷贝(Zero-Copy)

    首先介绍一下传统的网络I/O操作流程,从一个文件中读出数据并将数据传输到网络上的另一程序,其操作包含两个核心调用:

    File.read(fileDesc, buf, len);
    Socket.send(socket, buf, len);
    

    看上去好像挺简单的,但实际上从系统层面它做了以下4步操作:

    传统数据拷贝

    其中:

    1.read() 调用引发了一次从用户模式到内核模式的上下文切换。在内部,发出 sys_read()(或等效内容)以从文件中读取数据。直接内存存取(direct memory access,DMA)引擎执行了第一次拷贝,它从磁盘中读取文件内容,然后将它们存储到一个内核地址空间缓存区中。

    2.所需的数据被从读取缓冲区拷贝到用户缓冲区,read() 调用返回。该调用的返回引发了内核模式到用户模式的上下文切换(又一次上下文切换)。现在数据被储存在用户地址空间缓冲区。

    3.send() 套接字调用引发了从用户模式到内核模式的上下文切换。数据被第三次拷贝,并被再次放置在内核地址空间缓冲区。但是这一次放置的缓冲区不同,该缓冲区与目标套接字相关联。

    4.send() 系统调用返回,结果导致了第四次的上下文切换。DMA 引擎将数据从内核缓冲区传到协议引擎,第四次拷贝独立地、异步地发生 。

    整个过程共经历四次上下文切换,四次拷贝,其中2、3两步没有必要,应用程序只是起到缓存数据并将其传回到套接字的作用而以,别无他用。而零拷贝正是通过消除这些冗余的数据拷贝而提高性能的,零拷贝的一个核心调用变化就是将之前的 file.read() 和 socket.send() 方法替换成 transferTo() 方法:

    transferTo(position, count, writableChannel);
    

    transferTo() 方法将数据从文件通道传输到了给定的可写字节通道。在内部,它依赖底层操作系统对零拷贝的支持,在 UNIX 和各种 Linux 系统中,此调用被传递到 sendfile() 系统调用中,而此时系统操作变成了以下3步:

    零拷贝

    通过 transferTo() 方法,我们将上下文切换的次数从四次减少到了两次,将数据复制的次数从四次减少到了三次(其中只有一次涉及到了 CPU),不过这好像并未达到零拷贝的要求,在 Linux 内核 2.4 及后期版本中,套接字缓冲区描述符做了相应调整,使得 DMA 引擎可以直接把数据从内核缓冲区传输到协议引擎,从而消除了剩下的最后一次 CPU 拷贝,达到真正意义上的零拷贝。

    5.使用OS层面的PageCache

    Kafka 重度依赖底层操作系统提供的 PageCache 功能。当上层有写操作时,操作系统只是将数据写入 PageCache,同时标记 Page 属性为 Dirty。当读操作发生时,先从 PageCache 中查找,如果发生缺页才进行磁盘调度,最终返回需要的数据。实际上 PageCache 是把尽可能多的空闲内存都当做了磁盘缓存来使用。同时如果有其他进程申请内存,回收 PageCache 的代价又很小,所以现代的OS都支持 PageCache。

    使用 PageCache 功能同时可以避免在 JVM 内部缓存数据,JVM 为我们提供了强大的 GC 能力,同时也引入了一些问题不适用于 Kafka 的设计。

    • 如果在 Heap 内管理缓存,JVM 的 GC 线程会频繁扫描 Heap 空间,带来不必要的开销。如果 Heap 过大,执行一次 Full GC 对系统的可用性来说将是极大的挑战。
    • 所有在 JVM 内的对象都不免带有一个 Object Overhead (千万不可小视),内存的有效空间利用率会因此降低。
    • 所有的 In-Process Cache 在OS中都有一份同样的 PageCache。所以通过将缓存只放在 PageCache,可以至少让可用缓存空间翻倍。
    • 如果 Kafka 重启,所有的 In-Process Cache 都会失效,而OS管理的 PageCache 依然可以继续使用。

    总结

    通过以上测试和分析我们得出,Kafka 的确是个性能十分出色的消息队列,但是评估一个消息队列是否适用并不能仅仅看性能,还需考量其可靠性、是否支持扩展以及是否支持事物等,再根据实际的业务场景做出相应取舍,下面附上各大MQ的功能对比。

    MQ RabbitMQ ActiveMQ ZeroMQ Redis Kafka
    语言 Erlang Java C/C++ C scala
    支持协议 AMQP STOMP (STOMP 1.0, STOMP 1.1 and STOMP 1.2)MQTTHTTP(有三种方式) AMQPMQTTOpenWireSTOMP zmq_ipc(本地进程间通信)基于Socket的通信协议:TCP、INROC 、IPC 、PGM 自定义redis协议 自定义
    持久化策略 本地磁盘文件 AMQ(磁盘文件)、KahaDB(本地数据库)、JDBC、LevelDB(本地数据库) 不支持持久化 支持磁盘持久化 磁盘持久化
    消息确认机制 有消息确认机制 AUTO_ACKNOWLEDGE = 1 自动确认CLIENT_ACKNOWLEDGE = 2 客户端手动确认 DUPS_OK_ACKNOWLEDGE = 3 自动批量确认SESSION_TRANSACTED = 0 事务提交并确认自定义的ACK_MODE:INDIVIDUAL_ACKNOWLEDGE = 4 单条消息确认 无,更像NIO模式下的非阻塞事件模式 有消息确认机制
    批处理 支持批量发送消息 支持批量发送消息 支持 支持 支持
    消息处理模式(push-pull) push 同时支持Push-pull 请求响应模式(同步)发布订阅模式(异步)单向管道模型 非严格意义push-pull Pull
    消息顺序性 单个消费者有序 单个消费者有序 有序 Topic单个partition内有序
    支持事务 支持 支持 支持
    集群策略 主从模式支持自动选主 1、消费者集群2、Broker集群(共享队列)3、主从模式(共享DB、KahaDB复制、共享文件系统) 主从 zookeeper分布式多节点
    容灾 主从、镜像队列 共享存储ZooKeeper协调 主从或codis 分区副本
    负载均衡 Rabbitmq-cluster或LVS或HAproxy Broker-Cluster(共享队列) LVS或codis 通过zookeeper调度topic分区
    管理界面 rabbitmqadmin ActiveMQ Web Console Redis-cli或redisLive kafka-run-class.sh

    参考

    Kafka 高性能吞吐揭秘

    MQ对比

    相关文章

      网友评论

          本文标题:Kafka性能测试及分析

          本文链接:https://www.haomeiwen.com/subject/iiwlpqtx.html