Kafka 的性能策略
in Note with 0 comment
Kafka 的性能策略
in Note with 0 comment

背景

工作中,一直在使用 Kafka,而且也在维护好几个 Kafka 集群,毫无疑问 Kafka 是当前最流行的消息队列中间件,是什么让它如何流行,受人青睐,接下来我会从性能角度来讲解 Kafka 为何如此受欢迎。

什么是 Kafka

首先,Kafka 是一个分布式的流式数据处理平台,它的重要功能有:

在大数据处理需求背景下,Kafka 必然会对以上功能进行性能优化,性能的优化要点/瓶颈在于:

传输效率

Kafka 性能提升主要是利用操作系统的IO优化技术,脱离 JVM 的内存局限。

为什么从操作系统说起呢?人们每天都在使用操作系统,反而普遍忽略的操作系统的作用,让我们回想起来,操作系统的一大作用是消除硬件差异,为用户程序提供统一标准的API,由此,大部分人使用IO停留在调用系统的read/write,后端工程师则会更多了解 NIO 的epoll/kqueue,让我们看看 Kafka 下面两种优化策略。

mmap

实际上,现代的操作系统已经对磁盘 IO 做了复杂的优化,Linux 下有一个常见的缩写名词 vfs,即虚拟文件系统(virtual file system),它对内存与外存(磁盘)进行映射,使读写速度得到提升,比如以下且不限于:

2018-12-01T14:54:14.png

以上的内存/磁盘映射的优化,这依赖于操作系统的预测策略,一般而言,往往是对磁盘的顺序访问,效率明显更高。

操作系统除了自动完成以上的过程,还提供 API mmap给用户主动映射文件到page cache,系统会将这一片page cache共享给用户程序的内存,用户程序不必提前alloc memory,直接读取页面缓存即可访问数据。于是,在频繁访问一个大文件时,比起单纯的writemmap是一个更好的选择,它减少了普通write过程中的用户态与内核态的上下文切换次数(反复复制缓存)。

Zero-Copy

上面我们认识了磁盘缓存的优化策略,那么对于另一个被频繁使用的IO对象——socket ,如何优化呢

在 Linux 2.1+ 就引入的sendfile系统调用,通过sendfile,我们可以把page cache的数据直接拷贝到socket cache中,仅仅将两者的文件描述符、数据的offsetsize传参给 sendfileDMA 引擎(Direct Memory Access,直接内存存取)会将内核缓冲区的数据拷贝到协议引擎中去,不需要经过用户态上下文,不需要用户程序准备缓存,于是用户缓存为零,这就是 Zero-Copy 技术。

#include<sys/sendfile.h>
ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);

2018-12-01T14:54:44.png

Zero-Copy 技术对 Java 程序来说无异于神兵降临,让缓存的大小与速度脱离了 JVM 的局限。

结合 Kafka 的使用场景,多个订阅者拉取消息,消息被多个不同的消费者拉取,使用 Zero-Copy 技术,先调用mmap读取磁盘数据到一份page cache,再多次调用sendfile将一份page cache 复制到不同的socket cache,整个复制过程在系统内核态中完成,极致的利用了操作系统的性能,瓶颈近乎限于硬件条件(磁盘读写、网络速度)。

消息质量

我们可以从三个纬度看 Kafka 如何保证消息的质量,分别是消息的实时性、可靠性、吞吐量。

空间换时间

Kafka为了解决网络请求过多的问题,生产者会合并多条消息再提交,降低网络IO的频繁,以牺牲一点延迟换取更高的吞吐量。

在实践中,我们可以为生产者客户端配置这个过程的参数,例如:设置消息大小上限为64字节,延迟时间为100毫秒,它的效果是:100毫秒内消息大小达到64字节就要立即发送,如果在100毫秒没有达到64字节,但是100毫秒时间到了,也要发送缓存中的消息。

分布式设计

之前分析了单机环境的策略(操作系统、通信IO),然后,在水平拓展上,Kafka有什么性能优化策略呢?

做一个分布式的消息系统,需要考虑什么呢?

如何利用分布式、多节点的优势,增强消息系统的吞吐量、容灾能力、灵活扩容的能力…

让我们将思路抽象化,消息是流动的水,单机下是一条水管,多节点下是一片自来水网络,为了使消息的流动更加稳健,我们得保证在消息流动的每一个环节都有所保障

先罗列消息流动的每一个环节

Kafka 如何应对这些环节呢?

在水平拓展上,Kafka将一个 Topic 下从生产者收集到的消息存放到多个分区(partition)上,分区数大于等于Kafka节点数(brokers),每一个分区最多分配一个消费者。

直观来看,分区(partition)、消费者(consumer)会发生一下几种情况的 rebalance

备份策略

除了水平拓展的分区,还要对总分区进行多个备份(Replicas),对一个分区设置一个 leader 多个 follower,由一个 leader 处理该分区的事务,follower 需要处于 ISR 状态(In Sync Replicas),一旦 leader 故障,通过在备份最新的 follower 中产生新的 leader

总结

The article has been posted for too long and comments have been automatically closed.