Java架构直通车——Kafka介绍和高性能原因

Kafka介绍

Kafka以前说过很多次了,包括了Kafka单独的介绍,Kafka与Fabric,这里知识简单说说。
Kafka的主要特点就是基于Pull模式来处理消息消费,追求高吞吐量,一开始的目的就是用于日志的收集和传输。
Kafka0.8版本开始支持复制,不支持事务,并且对消息的重复、丢失、错误没有严格要求(也可以做到严格要求,但是性能损失很大),适合产生大量数据的互联网服务的数据收集业务。

Kafka高性能原因


  1. 顺序写

比如说consumer顺序的去消费数据,并且不会删除已经消费过的数据从而避免磁盘的随机写。也就是必须要做到顺序写,Producer把消息发到了broker,然后消息会存到磁盘的某个点,这个点的位置就是offset,这个offset会被consumer去消费,消费的时候会记录下当前消费的位置在哪,然后基于这个offset去找下一个offset,只有这个过程才能充分利用磁盘的利用率。所以说,一般的MQ的设计都不允许去删除消息


  1. 使用Page Cache,能做到空中接力,高效读写

PageCache内部实现是很复杂的,它是操作系统实现的磁盘缓存,以此来减少对磁盘的IO操作,简单来说,就是把磁盘中的数据缓存到内存中,然后把对磁盘的访问变成对内存的访问。PageCache能够支持在单机的情况下,100K/s的吞吐量。

正常的文件读取流程,需要经过下面这4步(会经过4次copy):
在这里插入图片描述
在这里插入图片描述
通过上面的分析可以看出,第2、3次拷贝(也就是从内核空间到用户空间的来回复制)是没有意义的,数据应该可以直接从内核缓冲区直接送入Socket缓冲区。
在这里插入图片描述
在这里插入图片描述

而kafka主要是使用zerocopy(零拷贝):
它跟应用程序是没有关联的,也就是应用程序不参与copy。从而避免了重复copy的过程。硬件可以通过Scatter/Gather DMA直接从内核缓冲区中取得全部数据,不需要再从内核缓冲区向Socket缓冲区拷贝数据。
在这里插入图片描述

Kafka为什么不自己管理缓存,而非要用page cache?原因有如下三点:

  • JVM中一切皆对象,数据的对象存储会带来所谓object overhead,浪费空间;
  • 如果由JVM来管理缓存,会受到GC的影响,并且过大的堆也会拖累GC的效率,降低吞吐量;
  • 一旦程序崩溃,自己管理的缓存数据会全部丢失。

Kafka三大件(broker、producer、consumer)与page cache的关系可以用下面的简图来表示。
在这里插入图片描述
producer生产消息时,会使用pwrite()系统调用【对应到Java NIO中是FileChannel.write() API】按偏移量写入数据,并且都会先写入page cache里。consumer消费消息时,会使用sendfile()系统调用【对应FileChannel.transferTo() API】,零拷贝地将数据从page cache传输到broker的Socket buffer,再通过网络传输。

图中没有画出来的还有leader与follower之间的同步,这与consumer是同理的:只要follower处在ISR中,就也能够通过零拷贝机制将数据从leader所在的broker page cache传输到follower所在的broker。

同时,page cache中的数据会随着内核中flusher线程的调度以及对sync()/fsync()的调用写回到磁盘,就算进程崩溃,也不用担心数据丢失。另外,如果consumer要消费的消息不在page cache里,才会去磁盘读取,并且会顺便预读出一些相邻的块放入page cache,以方便下一次读取。

由此我们可以得出重要的结论:如果Kafka producer的生产速率与consumer的消费速率相差不大,那么就能几乎只靠对broker page cache的读写完成整个生产-消费过程,磁盘访问非常少。这个结论俗称为“读写空中接力”。


  1. 后台异步、主动Flush
  2. 预读策略,IO调度

这两点以后有机会再做介绍。

发布了392 篇原创文章 · 获赞 326 · 访问量 16万+

猜你喜欢

转载自blog.csdn.net/No_Game_No_Life_/article/details/104462147
今日推荐