其他
为什么 Kafka 这么快?
作者 | Emil Koutanov
译者 | 明明如月,责编 | 郭芮
出品 | CSDN(ID:CSDNnews)
注意: 对于那些不熟悉“实时”这个词的人来说,“实时”并不意味着“快速” ,而是意味着“可预测”。 具体来说,实时意味着对完成一项行动所花费的时间有一个确定的上限,也就是所谓的最后期限。如果系统作为一个整体不能每次都能在这个最后期限内完成,它就不能被归类为实时。能够在概率容差范围内运行的系统被标记为“接近实时”。就吞吐量而言,实时系统通常比近实时或非实时系统慢。
broker 性能
__consumer_offsets
topic 中。另外,这是一个只追加的操作,所以很快。这个主题(topic)的内容所占空间也会在后台压缩 (使用 Kafka 的压缩特性) ,只保留任意给定消费者组的最后已知偏移量。fsync
; 对 ACK 的唯一要求是记录已被写入 I/O 缓冲区。这是一个鲜为人知的事实,但却是一个至关重要的事实: 事实上,正是这个处理技巧让 Kafka 的表现看起来像是一个内存中的队列ー实际上 Kafka 是一个有磁盘支持的内存队列 (受缓冲区 / pagecache 大小的限制)。fsync
的非阻塞 I/O 方法和冗余的同步副本,Kafka 得到了高吞吐量、可靠性和可用性的结合。java.nio.channels.FileChannel
的 transferTo ()
方法 ) 在 Linux 和 UNIX 系统上解决了这个问题。此方法允许将字节从源信道传输到接收信道,而不涉及作为传输中介的应用程序。为了体会 NIO 的优势,我们来看看传统的方法,源信道被读入一个字节缓冲区,然后作为两个独立的操作写入一个接收信道:Socket.send(socket, buf, len);
最初的
read()
造成了用户态到内核态的切换。当文件被读取后,它的内容将被 DMA(直接内存存取)拷贝到内核地址空间的缓冲区中。注意,这个缓冲区和代码里使用的缓冲区是不同的。在
read()
函数返回之前,内容将从内核缓冲区拷贝到用户缓冲区中。此时,我们的应用程序就可以读取这个文件的内容了。后来的
send()
函数调用从用户态又切换到内核态,即将用户空间缓冲区的数据拷贝到内核地址空间,此时将拷贝到目标 socket 相关的缓冲区中。在这背后, DMA 将接管这个操作,异步将数据从内核缓冲区拷贝到协议栈中。这里的send()
函数并不会等待这个过程结束才返回。send()
返回,又从内核态切换到用户态。
transferTo ()
方法指示块设备通过 DMA 引擎将数据读入读缓冲区。然后,这个缓冲区将另一个内核缓冲区拷贝到套接字中。最后,通过 DMA 将套接字缓冲区复制到 NIC (网络接口控制器) 缓冲区。gather
操作的网络接口卡上,后者可以作为进一步的优化来实现。这一点如下图所示:transferTo ()
方法会让设备通过 DMA 引擎将数据读入内核读缓冲区中。但是,使用 gather 操作时,读缓冲区和套接字缓冲区之间不存在拷贝。相反,NIC 将得到一个指向读缓冲区的指针,以及由 DMA 清空的偏移量和长度。这种模式下,CPU 不会参与到缓冲区拷贝中。流并行性
主题分区模型。应该对主题进行分区,以实现最大化独立事件子流的数量。换句话说,记录顺序只能在绝对必要的情况下才保留。如果任何两个记录之间没有因果关系,那么它们就不应该绑定到同一个分区上。这意味着它们可以使用不同的键,因为 Kafka 将使用一个记录的键作为散列源会映射到一致的分区中。
组中消费者的数量。你可以增加消费者的数量,以便分担消息消费的负担。消费者数最多可达主题中分区的数量。(如果你愿意,您可以拥有更多的消费者,但是分区计数将对至少获得一个分区分配的活动消费者的数量设置一个上限; 剩余消费者将保持空闲状态。) 注意,消费者可以是进程也可以是线程。根据消费者执行的工作负载类型,你可以在线程池中使用多个单独的消费者线程或进程来消费记录。