使用批量消息提升服务端处理能力
- 生产者这端,kafka不会将生产的消息立马发出去,而是会先放在内存中缓存起来,选择合适的时机(或许到达某个时间,或许攒够一定数量)把缓存中的所有消息组成一批,一次性发给Broker。
- 在Kafka的服务端,也就是Broker这一端,不会把一批消息还原成多条消息,再一条条处理。而是直接把一批消息当做一个批消息,发到消费端。
- 到了消费端,消费者把批消息解开,再一条条交给业务代码处理。
- 构建批消息和解开批消息分别在发送端和消费端的客户端完成,不仅减轻了 Broker 的压力,最重要的是减少了 Broker 处理请求的次数,提升了总体的处理能力
使用顺序读写提升磁盘IO性能
- 顺序读写性能要比随机读写快几倍到几十倍。
- 磁盘读写数据时,要先寻址(即找到数据在磁盘上的物理位置),然后再进行数据读写。如果是机械硬盘,要移动磁头。
- 而顺序读写省去了大部分寻址时间,只要寻址一次,就可以连续读写下去。
- Kafka充分利用了这个特性,对于每个分区,从生产者收到的消息,都顺序写入对应的log文件中,一个文件写到固定大小,就开启下一个文件。消费的时候,也是从某个全局位置开始,也就是某个log文件中的某个位置开始,顺序把消息读出来。
利用 PageCache 加速消息读写
- PageCache是现代操作系统都具有的一项基本特性,通俗的说就是操作系统在内存中给磁盘文件简历的缓存。
- 应用程序写入文件时,操作系统会先把数据写入到内存中的PageCache,然后再一批批写到磁盘。
- 读取文件时,也是从PageCache中读取,
如果PageCache中没有数据,操作系统会引发一个缺页中断,应用程序会被阻塞,等待操作系统把数据从文件复制到PageCache后,应用程序再继续读取PageCache,这个过程就会比较慢
- 应用程序在使用完某块PageCache后,操作系统并不会立刻清除,而是尽可能使用空闲物理内存保存,除非系统内存不够了,操作系统才会清理掉一部分PageCache。清理算法类似LRU。
- 一般来讲,消息写入到服务端后就会被立马消费,命中PageCache的概率非常高。消费读消息都会命中 PageCache,带来的好处有两个:一个是读取的速度会非常快,另外一个是,给写入消息让出磁盘的 IO 资源,间接也提升了写入的性能。
ZeroCopy:零拷贝技术
- 在服务端,处理消费的大致逻辑是这样的:
- 首先,从文件中找到消息数据,读到内存中;
- 然后,把消息通过网络发给客户端。
- 这个过程中,数据实际上做了 2 次或者 3 次复制:
- 从文件复制数据到 PageCache 中,如果命中 PageCache,这一步可以省掉;
- 从 PageCache 复制到应用程序的内存空间中,也就是我们可以操作的对象所在的内存;
- 从应用程序的内存空间复制到 Socket 的缓冲区,这个过程就是我们调用网络应用框架的 API 发送数据的过程。
- Kafka 使用零拷贝技术可以把这个复制次数减少一次,上面的 2、3 步骤两次复制合并成一次复制。直接从 PageCache 中把数据复制到 Socket 缓冲区中,这样不仅减少一次数据复制,更重要的是,由于不用把数据复制到用户内存空间,DMA 控制器可以直接完成数据复制,不需要 CPU 参与,速度更快。
- 下面是这个零拷贝对应的系统调用
#include <sys/socket.h>
ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);
- 它的前两个参数分别是目的端和源端的文件描述符,后面两个参数是源端的偏移量和复制数据的长度,返回值是实际复制数据的长度。