乐趣区

关于java:Kafka为什么这么快

Kafka 是一个基于公布 - 订阅模式的音讯零碎,它能够在多个生产者和消费者之间传递大量的数据。Kafka 的一个显著特点是它的高吞吐率,即每秒能够解决百万级别的音讯。那么 Kafka 是如何实现这样高得性能呢?本文将从七个方面来剖析 Kafka 的速度劣势。

  • 零拷贝技术
  • 仅可追加日志构造
  • 音讯批处理
  • 音讯批量压缩
  • 消费者优化
  • 未刷新的缓冲写入
  • GC 优化

以下是对本文中应用得一些英文单词得解释:

Broker:Kafka 集群中的一台或多台服务器统称 broker
Producer:音讯生产者
Consumer:音讯消费者
zero copy:零拷贝

1. 零拷贝技术

零拷贝技术是指在读写数据时,防止将数据在内核空间和用户空间之间进行拷贝,而是间接在内核空间进行数据传输。对于 Kafka 来说,它应用了零拷贝技术来减速磁盘文件的网络传输,以进步读取速度和升高 CPU 耗费。下图阐明了数据如何在生产者和消费者之间传输,以及零拷贝原理。

步骤 1.1~1.3:生产者将数据写入磁盘
步骤 2:消费者不应用零拷贝形式读取数据

2.1:数据从磁盘加载到 OS 缓存

2.2:将数据从 OS 缓存复制到 Kafka 应用程序

2.3:Kafka 应用程序将数据复制到 socket 缓冲区

2.4:将数据从 socket 缓冲区复制到网卡

2.5:网卡将数据发送给消费者

步骤 3:消费者以零拷贝形式读取数据

3.1:数据从磁盘加载到 OS 缓存

3.2:OS 缓存通过 sendfile() 命令间接将数据复制到网卡

3.3:网卡将数据发送到消费者

能够看到,零拷贝技术防止了多余得两步操作,数据间接从 OS 缓存复制到网卡再到消费者。这样做的益处是极大地提高了 I / O 效率,升高了 CPU 和内存的耗费。

举荐博主开源的 H5 商城我的项目 waynboot-mall,这是一套全副开源的微商城我的项目,蕴含三个我的项目:经营后盾、H5 商城前台和服务端接口。实现了商城所需的首页展现、商品分类、商品详情、商品 sku、分词搜寻、购物车、结算下单、支付宝 / 微信领取、收单评论以及欠缺的后盾治理等一系列性能。技术上基于最新得 Springboot3.0、jdk17,整合了 MySql、Redis、RabbitMQ、ElasticSearch 等罕用中间件。分模块设计、简洁易保护,欢送大家点个 star、关注博主。

github 地址:https://github.com/wayn111/waynboot-mall

2. 仅可追加日志构造

Kafka 中存在大量的网络数据长久化到磁盘(生产者到代理)和磁盘文件通过网络发送(代理到消费者)的过程。这一过程的性能会间接影响 Kafka 的整体吞吐量。为了优化 Kafka 的数据存储和传输,Kafka 采纳了一种仅可追加日志构造形式来长久化数据。仅可追加日志构造是指将数据以程序追加(append-only)的形式写入到文件中,而不是进行随机写入或更新。这样做的益处是能够缩小磁盘 I/O 的开销,进步写入速度。

人们普遍认为磁盘的读写速度很慢,但实际上存储介质(尤其是旋转介质)的性能很大水平上取决于拜访模式。常见的 7,200 RPM SATA 磁盘上的随机 I / O 的性能要比程序 I / O 慢 3 ~ 4 个数量级。此外,古代操作系统提供了预读和提早写入技术,能够事后取出大块的数据,并将较小的逻辑写入组合成较大的物理写入。因而,即便在闪存和其余模式的固态非易失性介质中,随机 I/O 和程序 I/O 的差别依然很显著,只管与旋转介质相比,这种差异性曾经很小了。

3. 音讯批处理

Kafka 的高吞吐率设计的外围要点之一是批处理,即 Kafka 在音讯发送端和接收端都引入了一个缓冲区,将多条音讯打包成一个批次(Batch),而后一次性发送或接管。这样做的益处是能够缩小网络申请的次数,缩小了网络压力,进步了传输效率。

Kafka 的音讯批处理优化次要波及以下几个方面:

发送端(Producer)

Kafka 的 Producer 只提供了单条发送的 send() 办法,并没有提供任何批量发送的接口。当调用 send() 办法发送一条音讯之后,无论是同步还是异步发送,这条音讯不会立刻发送进来,而是先放入到一个双端队列中,而后 Kafka 应用一个异步线程从队列中成批发送音讯。

Kafka 提供了以下几个参数来管制发送端的批处理策略:

  • batch.size:指定每个批次能够收集的音讯数量的最大值。默认是 16KB。
  • buffer.memory:指定每个 Producer 能够应用的缓冲区内存的总量。默认是 32MB。
  • linger.ms:指定每个批次能够期待的工夫的最大值。默认是 0ms。
  • compression.type:指定是否对每个批次进行压缩,以及应用哪种压缩算法。默认是 none。

接收端(Broker)

Kafka 的 Broker 在接管到 Producer 发送过去的批次后,不会把批次再还原成多条音讯,而是间接将整个批次写入到磁盘中。这样做的益处是能够缩小磁盘 I/O 的开销,进步写入速度。

Kafka 利用了操作系统提供的内存映射文件(memory mapped file)性能,将文件映射到内存中,使得对文件的读写操作就相当于对内存的读写操作。这样就防止了用户空间和内核空间之间的数据拷贝,也防止了零碎调用的开销。

生产端(Consumer)

Kafka 的 Consumer 在从 Broker 拉取数据时,也是以批次为单位进行传递的。Consumer 从 Broker 拉到一批音讯后,客户端把批次解开,再一条一条交给用户代码解决。

Kafka 提供了以下几个参数来管制生产端的批处理策略:

  • fetch.min.bytes:指定每次拉取申请至多要获取多少字节的数据。默认是 1B。
  • fetch.max.bytes:指定每次拉取申请最多能获取多少字节的数据。默认是 50MB。
  • fetch.max.wait.ms:指定每次拉取申请最多能期待多长时间。默认是 500ms。
  • max.partition.fetch.bytes:指定每个分区每次拉取申请最多能获取多少字节的数据。默认是 1MB。

4. 音讯批量压缩

音讯批量压缩通常与音讯批处理一起应用。Kafka 会将多个音讯打包成一个批次(Batch),并对批次进行压缩(例如应用 gzip 或 snappy 算法),而后再发送给消费者。这样做的益处是能够节俭网络带宽,进步传输效率。

当然,压缩也有肯定的代价,即须要耗费 CPU 资源来进行压缩和解压缩。然而对于 Kafka 这样的高吞吐量的零碎来说,网络带宽往往是更大的瓶颈,所以压缩是值得的。

Kafka 还提供了一种灵便的压缩策略,即能够让生产者、代理和消费者之间协商压缩格局和级别。生产者能够抉择是否对音讯进行压缩,以及应用哪种压缩算法;代理能够抉择是否保留生产者压缩的音讯,或者对其进行从新压缩;消费者能够抉择是否对收到的音讯进行解压缩。这样能够依据不同的场景和需要来均衡性能和资源的耗费。

5. 消费者优化

Kafka 的消费者是基于拉模式(pull)的,即消费者被动向服务器申请数据,而不是服务器被动推送数据给消费者。这样做的益处是能够让消费者本人管制生产的速度和机会,也能够加重服务器的累赘,进步整体的吞吐量。

Kafka 的消费者所实现的性能是比拟简洁的,即它们不须要保护太多的状态和资源,也不须要和服务器进行简单的交互。Kafka 的消费者只须要做以下几件事:

  • 订阅一个或多个主题(topic),并退出一个消费者组(consumer group)。
    向群组协调器(group coordinator)发送心跳,表明本人还活着,并参加分区再平衡(partition rebalance)。
  • 向分区所在的代理(broker)发送拉取申请(fetch request),获取音讯数据。
  • 提交本人生产到的偏移量(offset),以便在呈现故障时复原生产地位。

能够看到,Kafka 的消费者并不需要保留音讯数据,也不须要对音讯进行确认或回复,也不须要解决重试或反复的问题。这些都由服务器端来负责。Kafka 的消费者只须要关注如何从服务器获取数据,并进行业务解决即可。

6. 未刷新的缓冲写入

Kafka 在写入数据时,应用了一种未刷新(flush)的缓冲写入技术,即它不会立刻将数据写入硬盘,而是先写入内存缓存中,而后由操作系统在适当的时候刷新到硬盘上。这样做的益处是能够进步写入速度,缩小磁盘 I/O 的开销。

Kafka 利用了操作系统提供的内存映射文件(memory mapped file)性能,将文件映射到内存中,使得对文件的读写操作就相当于对内存的读写操作。这样就防止了用户空间和内核空间之间的数据拷贝,也防止了零碎调用的开销。

当生产者向 Kafka 发送音讯时,Kafka 会将音讯追加到内存映射文件中,并返回一个确认给生产者。此时音讯并没有真正写入硬盘,而是由操作系统负责将内存中的数据刷新到硬盘上。操作系统会依据一些策略来决定何时刷新数据,例如定期刷新、缓存满了刷新、零碎闲暇时刷新等。

当然,这种技术也有肯定的危险,即如果操作系统在刷新数据之前产生解体或断电,那么内存中未刷新的数据就会失落。为了解决这个问题,Kafka 提供了一些参数来管制刷新策略,例如:

  • log.flush.interval.messages:指定多少条音讯后强制刷新数据。
  • log.flush.interval.ms:指定多少毫秒后强制刷新数据。
  • producer.type:指定生产者是同步还是异步模式。同步模式下,生产者会期待服务器刷新数据后再返回确认;异步模式下,生产者不会期待服务器刷新数据,而是立刻返回确认。

7. GC 优化

Kafka 作为一个 Java 编写得高性能的分布式音讯零碎,它须要解决大量的数据读写和网络传输。这些操作都会波及到 Java 虚拟机(JVM)的内存治理和垃圾回收(GC)机制。如果 GC 不合理或不及时,就会导致 Kafka 的性能降落,甚至呈现内存溢出或频繁的进展。为了帮忙使用者优化 GC,Kakfa 有如下倡议。

堆内存大小

堆内存是 JVM 用来存储对象实例的内存区域,它会受到 GC 的治理和回收。堆内存的大小会影响 Kafka 的性能和稳定性,如果堆内存太小,就会导致频繁的 GC,影响吞吐量和提早;如果堆内存太大,就会导致 GC 工夫过长,影响响应速度和可用性。

通常来说,Kafka 并不需要设置太大的堆内存,因为它次要依赖于操作系统的文件缓存(page cache)来缓存和读写数据,而不是将数据保留在堆内存中。因而 Kafka 倡议将堆内存大小设置为 4GB 到 6GB 之间。

堆外内存大小

堆外内存是 JVM 用来存储非对象实例的内存区域,它不会受到 GC 的治理和回收。堆外内存次要用于网络 I/O 缓冲区、间接内存映射文件、压缩库等。

Kafka 在进行网络 I/O 时,会应用堆外内存作为缓冲区,以缩小数据在用户空间和内核空间之间的拷贝。同时,Kafka 在进行数据压缩时,也会应用堆外内存作为长期空间,以缩小 CPU 资源的耗费。

因而,堆外内存对于 Kafka 的性能也很重要,如果堆外内存不足,就会导致缓冲区调配失败或压缩失败,影响吞吐量和提早。通常来说,Kafka 倡议将堆外内存大小设置为 8GB 左右。

GC 算法和参数

GC 算法是 JVM 用来回收无用对象占用的堆内存空间的办法,它会影响 Kafka 的进展工夫和吞吐量。GC 算法有多种抉择,例如串行 GC、并行 GC、CMS GC、G1 GC 等。

不同的 GC 算法有不同的优缺点和实用场景,例如串行 GC 适宜小型利用和低提早场景;并行 GC 适宜大型利用和高吞吐量场景;CMS GC 适宜大型利用和低进展工夫场景;G1 GC 适宜大型利用和均衡进展工夫和吞吐量场景等。

通常来说,Kafka 倡议应用 G1 GC 作为默认的 GC 算法,因为它能够在保障较高吞吐量的同时,管制进展工夫在 200ms 以内。同时,Kafka 还倡议依据具体情况调整一些 GC 参数,例如:

  • -XX:MaxGCPauseMillis:指定最大进展工夫指标,默认是 200ms。
  • -XX:InitiatingHeapOccupancyPercent:指定触发并发标记周期的堆占用百分比,默认是 45%。
  • -XX:G1ReservePercent:指定为拷贝存活对象预留的空间百分比,默认是 10%。
  • -XX:G1HeapRegionSize:指定每个堆区域的大小,默认是 2MB。

本文参考

  • https://medium.com/swlh/why-kafka-is-so-fast-bde0d987cd03
  • https://blog.bytebytego.com/p/why-is-kafka-fast
  • https://blog.csdn.net/csdnnews/article/details/104471147

总结

最初感激大家浏览,心愿本文能对你有所帮忙.

关注公众号【waynblog】每周分享技术干货、开源我的项目、实战经验、高效开发工具等,您的关注将是我的更新能源!

退出移动版