【Kafka官方文档翻译】5.4.3. 效率

原文地址:https://kafka.apache.org/0101/documentation.html#maximizingefficiency

我们在效率上投入了众多的努力,一个我们的主要用例是具有大吞吐量的 web 活动日志,每页面的每次访问都会产生好几十次的写,进一步,我们假定每次消息发布,至少会被一个消费者读取(经常情况下是多个消费者),因此,我们努力使消费消息的代价尽可能小。
  从构建一些相似的系统的经验中,我们也发现,有效的多租户操作是提升性能的关键。下游的基础服务很容易由于程序的很小的使用错误成为瓶颈,例如,一些小的变化很常导致一些新的问题,我们可以非常快速在程序发布到基础平台前,进行迭代测试,这对需要在集中式的集群里跑几十个,几千个应用时,程序每天都在变动时非常有用。
  前面一个章节我们讨论了磁盘的性能,低效率的磁盘访问模式就忽略不说了,这里在系统上还有两个可能会导致效率低下的地方:很多小的 I/O 操作和过多的字节拷贝。
  小 I/O 问题在客户端和服务器端都会发生,在服务器端有它自己的存储操作。
  为了避免这个问题,我们的通讯协议正是基于消息集合这个概念构建的,很容易把多个消息组合起来。这样允许网络组合消息后进行发送,而不是每次发送一条信息,减少网络的来回开销。服务器也是每次写入一堆数据到日志中,消费者也是每次线性读取一堆数据。
  这种简单的优化可以提升大量的性能,批量处理导致大的网络数据包,大的磁盘顺序读写,连续的内存块等等。所有的这些能把 kafka 的间接性的随机消息写改成线性写入后,发送给消费者。
  另外一个低效率的地方是字节拷贝。在消息吞吐量不多的时候这不是一个问题,但在高负载下的影响是非常显著。为了避免这种情况,我们在生产者、服务器和消费者间使用一个标准化的二进制消息格式(这样数据块可以在它们之间直接进行传输而不需要再做修改)。
  服务器端使用文件的形式维护消息日志,所有的消息都按生产者和消费者使用的格式顺序写入到磁盘中,维护这样的格式需要优化最常用的一些操作:对持久日志块的网络传输。现在的unix操作系列通常都有提供高效的优化代码直接把数据从缓存页发送到socket,在linux下使用 sendfile 的系统调用。
  如果要理解一下sendfile调用的功效,需要了解下正常情况下数据从文件发送到socket的过程:

  • 1.操作系统从磁盘读取数据到系统内核空间的缓存页中
  • 2.应用从内核空间读取数据到用户空间缓冲区中
  • 3.应用把数据写回到内核空间的socket缓冲区中
  • 4.系统拷贝 socket 缓冲区的数据到网卡缓冲区,然后由网卡发送数据到网络中
      这很明显效率很低,有4次拷贝还有2次系统调用,如果使用sendfile命令,重新拷贝运行系统直接把数据从缓存页拷贝到网络,优化后,最终只需要一次从缓存页到网卡缓冲区拷贝。
      我们预期一个常见消费方式是使用多个消费者同时消费一个主题,使用上面提到的 zero-copy 的优化方式,数据只被拷贝到页缓存一次,并被多次消费,而不是缓存到(用户空间的)内存中,然后在每次消费时拷贝到系统内核空间中。这可以使消费者消费消息的速度达到网络连接的速度。
      组合页缓存和sendfile机制后,kafka集群在跟上消费者消费的同时,让你觉得好像没有多少的磁盘读活动,因为大部分的数据响应需求都是从缓存获取的。
      如果想要知道更多关于java对sendfile和zero-copy的支持, 可以阅读这篇文章 article.

端到端的数据压缩

在大部分情况下,瓶颈不会是cpu或磁盘,而是网络带宽。这个在数据中心之间建立需要跨越广域网发送消息的数据管道时更为明显,当然用户可以独立于 kafka 自己做消息压缩,但是这有可能由于消息类型冗余,导致压缩比例很低(例如json的字段名或web中的用户代理日志或常用的字符串值),有效的压缩方式应该是允许压缩重复的消息,而不是分别压缩单个消息。
  kafka 通过递归的消息集合支持这样的操作。一批的消息可以被收集在一起后压缩,并发送到服务器端。这样被压缩的一批数据,日志也是使用压缩的格式,只有在消费者消费的时候才会被解压。
  kafka 支持 GZIP,Snappy and LZ4 压缩协议,更多关于压缩的细节可以查看这里 here。

你可能感兴趣的:(【Kafka官方文档翻译】5.4.3. 效率)