这篇文章就是讲讲Kafka的一些架构设计原则,也是互联网公司面试中非常高频的技术考点。Kafka是一种高吞吐、低延迟、高并发、高性能的消息中间件,广泛应用于大数据领域。一个配置良好的Kafka集群甚至可以达到每秒几十万甚至上百万的超高并发写入。那么Kafka是如何做到如此高的吞吐量和性能的呢?本篇,我们就来一点一滴的讲。1、页面缓存技术+磁盘顺序写入首先,Kafka在每次接收到数据时都会将数据写入磁盘,如下图所示。那么这里不禁会有一个疑问,如果数据是基于磁盘存储的,数据频繁写入磁盘文件,性能会不会很差?大家肯定觉得磁盘写性能极差。没错,如果真的像上图那么简单,那么性能确实比较差。但其实Kafka这里有一个非常优秀的设计,就是为了保证数据写入的性能。首先,Kafka是基于操作系统的pagecache来实现文件写入的。操作系统本身有一层缓存,叫做pagecache,是内存中的缓存。我们也可以称之为oscache,意思是操作系统自己管理的缓存。写入磁盘文件时,可以直接写入os缓存,也就是只写入内存,然后由操作系统决定何时真正将os缓存中的数据刷入磁盘文件。仅仅这一步就可以大大提高磁盘文件的写入性能,因为实际上这相当于写入内存,而不是写入磁盘。请看下图。然后另一个就是kafka写数据的时候,很关键的一点,他是按照磁盘顺序写的。也就是说,仅将数据附加到文件末尾,而不是在文件中的随机位置修改数据。普通机械盘如果随便写,也就是找文件中的某个位置写入数据,性能极差。但是如果你通过追加到文件末尾的方式顺序写入数据,那么这种磁盘顺序写入的性能基本上可以和写入内存本身的性能相近。所以大家知道,在上图中,Kafka在写数据的时候,一方面是基于OS层面的pagecache来写数据,所以性能非常高,本质是写内存。另一种是它采用磁盘顺序写入,所以即使数据刷入磁盘,性能也非常高,类似于写入内存。基于以上两点,Kafka实现了写入数据的超高性能。那么想一想,如果Kafka写一条数据需要1毫秒,它每秒能写1000条数据吗?但是如果kafka的性能极高,写一条数据只需要0.01毫秒?那么是否有可能每秒写入100,000个数字?所以,保证每秒写入几万甚至几十万条数据的核心点就是尽可能提高每一次数据写入的性能,让单位时间和吞吐量下写入更多的数据可以改进。.2、零拷贝技术写完了,再来说说消费。大家应该都知道,我们经常需要从Kafka消费数据,所以在消费的时候,我们其实需要从Kafka的磁盘文件中读取一段数据,发送给下游的消费者,如下图所示。那么如果频繁的从磁盘读取数据然后发送给消费者,性能瓶颈在哪里呢?假设如果Kafka不做任何优化,很简单的从磁盘读取数据发送给下游消费者,那么大致流程如下:首先检查要读取的数据是否在os缓存中,如果不是,从磁盘读取文件中的数据后,放入os缓存中。然后从操作系统的OS缓存中拷贝数据到应用进程的缓存中,再从应用进程的缓存中拷贝数据到操作系统层面的Socket缓存中,最后从Socket缓存中提取数据并发送给网卡,最后发送给下游消费。整个过程如下图所示:如果你看上图,你可以明显看到有两个不必要的副本!一旦从操作系统的缓存中拷贝到应用进程的缓存中,再从应用缓存中拷贝回操作系统的socket缓存中。而且,为了执行这两个副本,中间发生了几次上下文切换。一会儿是应用程序在执行,一会儿是上下文切换到操作系统执行。所以这种方式读取数据是比较耗性能的。为了解决这个问题,Kafka在读取数据的时候引入了零拷贝技术。也就是说直接将操作系统缓存中的数据发送给网卡再传输给下游消费者,跳过了中间复制数据的两步,只会在Socket中复制一个描述符缓存,并且没有数据将被复制到套接字缓存。请看下图体验一下这个微妙的过程:通过零拷贝技术,不需要先将OS缓存中的数据拷贝到应用缓存中,再从应用缓存中拷贝到Socket缓存中。两个副本都被省略,所以称为零副本。Socket缓存只是将数据描述符复制过去,然后数据直接从os缓存中发送到网卡。这个过程大大提高了数据消费时读取文件数据的性能。而且你会注意到,从磁盘读取数据的时候,你会先查看os缓存内存中是否有。如果是这样的话,读取的数据实际上是直接从内存中读取的。如果Kafka集群调优好,会发现大量数据直接写入os缓存,读取数据时再从os缓存中读取。相当于Kafka完全基于内存提供数据写入和读取,所以整体性能会非常高。3.最后的总结通过本文中Kafka底层页面缓存技术的使用,磁盘顺序写入的思想,以及零拷贝技术的使用,你应该明白,当每台Kafka机器写入和读取数据时在底层采用了什么样的思路,为什么它的性能可以这么高,达到每秒几十万的吞吐量。这种设计思维对我们自己设计中间件架构,或者出去面试的时候很有帮助。
