一、硬件上的选择

Kafka 本身对硬件没有特别的要求,它可以运行在任何系统中。

如果需要注重性能,需要考虑几个影响性能的因素:

  • 磁盘吞吐量和容量
  • 内存
  • 网络
  • CPU

在 Kafka 扩展到一定规模,由于需要更新大量的元数据,单个 broker 能够处理的分区数量会有一定的限制。确定了性能关注点,就可以在预算范围内选择最优的硬件配置。

1、磁盘吞吐量

生产者客户端的性能直接受到服务器端磁盘吞吐量的影响。生产者生成的消息必须被提交到服务器保存,大多数客户端在发送消息之后会一直等待,直到至少有一台服务器确认消息已成功提交为止。也就是说,磁盘写入的速度越快,生成消息的延迟就越低。

经验表明,机械硬盘更适用于高存储量但不经常被访问的集群,而如果有非常多的客户端连接,则固态硬盘是更好的选择。

2、磁盘容量

需要多大的磁盘容量取决于需要保留多少消息。如果服务器每天收到 1 TB 消息,需要保留 7 天,那么就需要 7 TB 的存储空间,另外还要为其他文件提供至少 10% 的额外空间。除此之外,还需要提供额外的缓冲区,用于应对消息流量的增长和波动。

在决定扩展 Kafka 集群规模时,存储容量是另一个需要考虑的因素。我们可以为主题设置多个分区,让集群的总流量均衡分布到整个集群。如果单个 broker 容量不足,则可以让其他 broker 提供可用的容量。

存储容量的选择也会收到集群复制策略的影响。

3、内存

一般来说,Kafka 消费者从分区的末尾(也就是在生产者成功写入数据之后)读取数据,即使出现了滞后,也不会滞后太多。消费者正在读取的消息会被放到系统的页面缓存中,这比从磁盘上重新读取的速度更快。因此,为系统提供更多的内存用于页面缓存可以提高消费者客户端的性能。

Kafka 本身不需要太多内存。一个每秒处理 150 000 条消息和每秒 200 MB 数据速率的 broker,只需要 5
GB 堆内存,剩下的系统内存用于页面缓存。

因为缓存了正在使用的日志片段,所以 Kafka 的性能得到了提升。这就是为什么不建议把 Kafka 同其他重要的应用程序部署在一起,因为它们需要共享页面缓存,从而会降低 Kafka 消费者的性能

4、网络

网络吞吐量决定了 Kafka 能够处理的最大数据流量,它和磁盘存储是制约 Kafka 伸缩规模的主要因素。

Kafka 支持多消费者,导致流入和流出的网络流量不均衡,从而让情况变得更加复杂。

对于给定的主题,一个生产者可能每秒写入 1 MB 数据,但可能同时存在多个消费者,这就需要数倍的流出网络流量。其他的操作,比如集群复制,也会占用网络流量。

如果网络接口达到饱和状态,那么集群的复制出现延时就在所难免,从而让集群变得不堪一击。

为了避免网络成为主要的制约因素,建议至少使用 10 GB 的网卡。配备 1 GB 网卡的老机器很容易达到饱和状态,不推荐使用。

5、CPU

与磁盘和内存相比,Kafka 对计算处理能力的要求相对较低,不过在一定程度上还是会影响整体的性能。

为了优化网络和节省磁盘空间,客户端会对消息进行压缩。服务器需要对消息进行解压缩,检查每条消息的校验和并分配偏移量,然后再将其压缩保存到磁盘上。这个过程是 Kafka 需要用到计算处理能力的地方。

不过不管怎样,这都不应该成为选择硬件的主要考虑因素,除非集群变得非常大,大到单个集群有数百个节点和数百万个分区。如果真的到了那个时候,则可以选择更高性能的 CPU,避免集群规模太过膨胀。

二、配置 Kafka 集群

使用集群的好处就是可以跨服务器进行负载均衡,还有就是可以使用复制功能来避免因单点故障造成的数据丢失。

1、需要多少个 broker

一个集群需要多少个 broker 取决于一下几个因素:

  • 磁盘容量
  • 单个 broker 的复制容量
  • CPU
  • 网络

1.1 磁盘空间

如果整个集群需要保留 10 TB 数据,每个 broker 可以存储 2 TB,那么至少需要 5 个 broker。另外,如果增加了复制系数,那么至少还需要多一倍的空间,具体取决于配置的复制系数是多少。复制系数是指一个 broker 的数据需要被复制到多少个其他 broker 上。如果上述的集群配置复制系数是 2,那么将至少需要 10 个 broker。

1.2 集群处理请求能力

假设有一个包含 10 个 broker 的集群,集群中有 100 多万个副本(也就是说,有 500 000 个分区,复制系数为 2),如果分区分布得均衡,那么每个 broker 大约有 100 000 个副本。这可能会导致生产者、消费者和控制器队列出现瓶颈。

在过去,官方的建议是每个 broker 的分区副本不超过 4000 个,每个集群的分区副本不超过 200 000 个。随着集群效率的提升,Kafka 可以被扩展到更大的规模。目前,建议每个broker 的分区副本不超过 14 000 个,每个集群的分区副本不超过 100 万个。

在大多数情况下,CPU 通常不是主要瓶颈,但如果有很多客户端连接和请求,则 CPU 可能也会成为瓶颈。在观察大型集群的 CPU 整体使用情况时,需要关注有多少个客户端和消费者群组,并进行相应的伸缩,以满足它们的需求,这样有助于确保更好的整体性能。

三、操作系统调优

可以通过调整虚拟内存、网络子系统和用来存储日志片段的磁盘挂载点等,来进一步提升 Kafka 的性能。

1、虚拟内存

一般来说,Linux 的虚拟内存会根据系统的工作负载进行自动调整。我们可以对交换分区的处理方式以及内存脏页做一些调整,让 Kafka 更好地处理工作负载。

对大多数应用程序(特别是那些依赖吞吐量的应用程序)来说,要尽量避免内存交换。内存页和磁盘之间的数据交换对 Kafka 各方面的性能都有重大影响。Kafka 大量使用了系统页面缓存,如果虚拟内存被交换到磁盘,则说明已经没有多余内存可以分配给页面缓存

一种避免内存交换的方法是不设置任何交换分区。内存交换不是必需的,不过它确实能够在系统发生灾难性错误时提供一些帮助。内存交换可以防止操作系统由于内存不足而突然终止进程。

2、磁盘

除了磁盘硬件设备和冗余磁盘阵列,文件系统是影响性能的另一个重要因素。

有很多种文件系统可供选择,对本地文件系统来说,Ext4(第四代扩展文件系统)和 XFS(扩展文件系统)最为常见。

XFS 是很多 Linux 发行版默认的文件系统,因为它只需要做少量调优就可以承担大部分的工作负载,表现要优于 Ext4。Ext4 也可以表现得很好,但需要做更多的调优,存在较大的风险,其中就包括设置更长的提交时间间隔(默认是 5),以便降低刷新频率。Ext4 引入了块分配延迟,一旦系统崩溃,更容易造成数据丢失和文件系统毁坏。XFS 也使用了分配延迟算法,不过比 Ext4 要安全些。

XFS 为 Kafka 提供了更好的性能,除了文件系统提供的自动调优,不需要再做额外的调优。另外,XFS 的批量磁盘写入的效率更高,所有这些组合在一起,提高了整体的 I/O 吞吐量。

不管使用哪一种文件系统来存储日志片段,都要合理设置挂载点的 noatime 属性。文件元数据包含 3 个时间戳:

  • 创建时间(ctime)
  • 最后修改时间(mtime)
  • 最后访问时间(atime)

在默认情况下,每次文件被读取后都会更新 atime,这会导致大量的磁盘写操作。atime 属性的用处并不大,除非应用程序想要知道某个文件在最近一次修改后有没有被访问过(对于这种情况可以使用 relatime)。

Kafka 没有用到 atime 这个属性,所以完全可以将其禁用。设置挂载点的 noatime 属性可以避免更新 atime,但又不影响 ctimemtime。当有大量磁盘写入时,通过设置 largeio 也有助于提升 Kafka 的效率。

3、网络

在默认情况下,系统内核并没有针对高速大流量网络传输做过优化,所以对需要处理大网络流量的应用程序来说,一般需要对 Linux 系统的网络栈进行调优。实际上,调整 Kafka 的网络配置与调整其他大部分 Web 服务器和网络应用程序的网络配置是一样的。首先,可以调整分配给 socket 读写缓冲区的默认内存和最大内存,这样可以显著提升大流量网络的传输性能。

四、生产环境部署注意事项

1、垃圾回收器

Kafka 是基于 Java 的,建议将 G1GC 作为 Kafka 默认的垃圾回收器。

在应用程序的整个生命周期中,G1GC 会根据工作负载进行自我调节,并且停顿时间是恒定的。它可以轻松收集大块堆内存,把堆内存分为若干小块区域,并不是每次都回收整个堆空间。

下面是两个 G1GC 的主要的性能调优参数:

  • MaxGCPauseMillis,该参数指定了每次垃圾回收的默认停顿时间。默认是 200 毫秒。
  • InitiatingHeapOccupancyPercent,该参数指定了在 G1GC 启动新一轮垃圾回收之前可以使用的堆内存百分比,默认是 45,也就是说在堆内存使用率达到百分之四十五之前,G1GC 不会启动垃圾回收。这个百分比包括新生代和老年代的内存。

Kafka 使用堆内存以及处理垃圾对象的效率是比较高的,可以把这些参数设置的小一些。

Kafka 是在 G1GC 之前发布的,它默认使用的是 CMS(并发标记和清除)垃圾回收器,以确保所有的 JVM 兼容。

2、共享 ZooKeeper

Kafka 使用 ZooKeeper 保存 broker、主题和分区的元数据。

只有当消费者群组成员或 Kafka 集群本身发生变化时才会向 ZooKeeper 写入数据。这些流量通常很小,所以没有必要为单个 Kafka 集群使用专门的 ZooKeeper 群组。

如果可以的话,除了让多个 Kafka 集群共享一个 ZooKeeper 群组,不建议再把 ZooKeeper 共享给其他应用程序。Kafka 对 ZooKeeper 的延迟和超时比较敏感,与 ZooKeeper 群组之间的一个通信中断都可能导致 Kafka 出现不可预测的行为。如果有多个 broker 与 ZooKeeper 断开连接,那么它们就会离线,进而导致分区离线。这也会给集群控制器造成压力,因为在发生中断一段时间后,当控制器尝试关闭 broker 时,会表现出细微的异常。其他应用程序也会给 ZooKeeper 群组造成压力,它们可能会大量使用 ZooKeeper 或者执行一些不恰当的操作。所以,最好让其他应用程序使用自己的 ZooKeeper 群组。

相关链接

OB tags

#Kafka