1 消息批处理
在Kafka内部,其实从Producer发送到Consumer接收,消息都是以“批”为单位处理的。
- Producer:在Kafka的客户端SDK中,Kafka的Producer只提供了单条发送的send()方法,但实际上Kafka的客户端SDK在实现消息发送逻辑的时候,采用了异步批量发送的机制。当你调用send()方法发送一条消息之后,无论你是同步发送还是异步发送,Kafka都不会立即就把这条消息发送出去。它会先把这条消息,存放在内存中缓存起来,然后选择合适的时机把缓存中的所有消息组成一批,一次性发给Broker。
- Broker:在Broker整个处理流程中,无论是写入磁盘、从磁盘读出来、还是复制到其他副本这些流程中,批消息都不会被解开,一直是作为一条“批消息”来进行处理的。
- Consumer:消息同样是以批为单位进行传递的,Consumer从Broker拉到一批消息后,在客户端把批消息解开,再一条一条交给用户代码处理。
2 顺序写磁盘
对于磁盘来说,它有一个特性,就是顺序读写的性能要远远好于随机读写。顺序读写相比随机读写省去了大部分的寻址时间,它只要寻址一次,就可以连续地读写下去,所以说,性能要比随机读写要好很多。
Kafka就是充分利用了磁盘的这个特性。它的存储设计非常简单,对于每个分区,它把从Producer收到的消息,顺序地写入对应的log文件中,一个文件写满了,就开启一个新的文件这样顺序写下去。消费的时候,也是从某个全局的位置开始,也就是某一个log文件中的某个位置开始,顺序地把消息读出来。
3 PageCache加速读写
PageCache是现代操作系统的一项基本特性。通俗地说,PageCache就是操作系统在内存中给磁盘上的文件建立的缓存。
- 写场景:应用程序在写入文件的时候,操作系统会先把数据写入到内存中的PageCache,然后再一批一批地写到磁盘上。
- 读场景:如果PageCache中有数据,那就直接读取;如果PageCache中没有数据,这时候操作系统会引发一个缺页中断,应用程序的读取线程会被阻塞,操作系统把数据从文件中复制到PageCache中,然后应用程序再从PageCache中继续把数据读出来,这个读的过程就会比较慢。
用户的应用程序在使用完某块PageCache后,操作系统并不会立刻就清除这个PageCache,而是尽可能地利用空闲的物理内存保存这些PageCache。清理的策略一般是LRU或它的变种算法,大致逻辑是:清除最久不使用的PageCache。
4 ZeroCopy零拷贝技术
Kafka的服务端在消费过程中,还使用了一种“零拷贝”的操作系统特性来进一步提升消费的性能。
在服务端,处理消费的大致逻辑是这样的:数据实际上做了2次或者3次复制,如果PageCache已经存在就是两次复制,否则三次
- 从文件复制数据到PageCache中,如果命中PageCache,这一步可以省掉;
- 从PageCache复制到应用程序的内存空间中,也就是我们可以操作的对象所在的内存;
- 从应用程序的内存空间复制到Socket的缓冲区,这个过程就是我们调用网络应用框架的API发送数据的过程。
Kafka使用零拷贝技术可以把这个复制次数减少一次,上面的2、3步骤两次复制合并成一次复制。直接从PageCache中把数据复制到Socket缓冲区中,这样不仅减少一次数据复制,更重要的是,由于不用把数据复制到用户内存空间,DMA控制器可以直接完成数据复制,不需要CPU参与,速度更快。