Kafka-高性能

文章目录

  • 1. 架构
  • 2. 磁盘
    • 2.1 零拷贝
    • 2.2 顺序写
    • 2.3 页缓存
  • 3. 网络
    • 3.1 高效的请求模型Reactor
    • 3.2 压缩与批处理
  • 参考

1. 架构

分区
Kafka的主题多分区机制,分区的副本、领导者副本一般均匀地分布在不同的Broker上,实现了并行处理,为Kafka提供了高伸缩性以及负载均衡能力。

基于ISR的动态复制方案
Kafka既不是完全的同步复制,也不是完全的异步复制,而是基于ISR实现了动态复制方案。同步副本是可变的,这样避免了较慢的副本拖慢整体速度,同时也兼顾了数据一致性。

2. 磁盘

2.1 零拷贝

Kafka需要在磁盘和网络之间传输大量的数据,在传统的非零拷贝场景下,比如使用InputStream和OutputStream将数据从磁盘发送到网络上,一共发生了4次数据拷贝,其中两次DMA拷贝,两次CPU拷贝,以及4次上下文切换:

  1. JVM发起read()调用,从用户态切换到内核态,DMA将数据从硬盘拷贝到内核读缓冲区
  2. CPU将数据从内核读缓冲区拷贝到应用程序内存。read()调用返回,线程从内核态切换到用户态。
  3. JVM发起write()调用,从用户态切换到内核态,CPU将数据从应用程序内存拷贝到内核的Socket缓冲区
  4. write()调用返回,线程从内核态切换到用户态。DMA将数据从Socket缓冲区拷贝到网卡缓存区(异步)。

Kafka-高性能_第1张图片

Kafka利用Java NIO库中FileChannel中的transformTo方法进行零拷贝,零指的是没有在内核空间和用户空间进行冗余的数据拷贝。在Linux平台上,其底层实现是sendfile。

sendfile完全避免了CPU拷贝,减少了数据搬运的次数;同时减少了用户态、内核态切换所导致的CPU上下文切换:

  1. JVM发起sendfile()调用,发生第一次上下文切换,DMA将数据从硬盘拷贝到内核读缓冲区
  2. 将内核读缓冲区的描述符信息附加到Socket缓冲区;
  3. sendfile()调用返回,发生第二次上下文切换。DMA根据描述符信息,将数据从内核读缓冲区拷贝到网卡缓冲区

Kafka-高性能_第2张图片

此外,大量使用零拷贝和页缓存,减少了对JVM堆的使用,避免了频繁的GC。

2.2 顺序写

Kafka使用只能追加写(Append-Only)的消息日志来保存数据,避免了缓慢的随机IO。每个消息日志又被分成若干个日志片段,当前消息只能被追加到最新的片段中,也就是活跃片段

Kafka并不会将已经被消费的消息立马删除,避免产生随机IO。数据的清理会根据留存时间、大小,将整个日志片段删除。

2.3 页缓存

保存在Broker上的数据格式与生产者发来的数据格式,以及发往消费者的数据格式是一致的。使用相同的数据格式来存储和网络传输,这是Kafka可以使用零拷贝和页缓存来加速处理请求的前提。

当Broker收到生产请求时,将消息写入操作系统的页缓存中,但并立即执行刷盘操作,这大大加快了数据的写入速度。Kafka的持久性是利用复制和分区多副本机制来保证的。

当Broker收到获取请求时,如果页缓存仍保存有所需要的数据,那么可以在零拷贝的基础上再减少一次磁盘到内核读缓存的数据拷贝,只需要一次拷贝即可处理获取请求。

3. 网络

3.1 高效的请求模型Reactor

Kafka Broler使用Reactor模式来处理请求。Reactor模式是事件驱动架构的一种实现方式,特别适用于客户端高并发请求服务端的应用场景。在该模型中,客户端请求发送给了Reactor,请求分发线程Acceptor将请求分发给工作线程。工作线程池负责实际的业务处理,可根据负载进行横向伸缩。Acceptor只负责请求分发,不实际具体业务处理,显得非常轻量级,能达到很高的吞吐量。

在Kafka中,Broker端的Acceptor线程以轮询的方式将请求分发给工作线程——网络线程。网络线程池并不对请求进行处理,而是将请求放入到共享的请求队列中。IO线程池从队列中取出请求,执行真正的处理:对于生产请求,将消息写入磁盘;对于获取请求,则从磁盘或是页缓存读取数据。IO线程处理完请求后,将响应放入对应的响应队列中,由相应的网络线程返回给客户端。Acceptor线程只负责请求分发,不负责响应回传。

在响应队列前还有一个炼狱组件(Purgatory),用来缓存延迟请求。所谓的延迟请求,就是指那些未满足条件不能立即处理的请求。比如,设置了发送确认acks = all的生产请求,那么该请求必须等所有同步副本收到消息后,才能返回。因此,处理该请求的IO线程不能立即处理该请求,而必须等待其它Broker的写入结果,那么它就会将请求暂存在炼狱组件中。稍后满足条件时再将请求取出,继续处理完成后放入响应队列中。

Kafka-高性能_第3张图片

3.2 压缩与批处理

Kafka不会操作具体的一条消息,而是在批次层面进行操作。批处理能够有效的提升吞吐量,降低网络负载。、

压缩进一步的提升了吞吐量,且批次越大效果越明显。

Kafka-高性能_第4张图片

参考

《Kakka核心技术与实战》

kafka高性能架构之道
kafka如何做到的高性能

你可能感兴趣的:(Kafka,kafka)