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

总结

Responses