现在,每个公司都在互联网系统中使用Kafka。 Kafka似乎是解决分布式并提高系统吞吐量的最佳松耦合解决方案之一。
我大约6年前开始使用Kafka。 当时,我在Aerohive工作。 为了解决企业wifi设备带来的大量日志,传统的消息传递系统RabbitMQ和ActiveMQ已经不堪重负。
此时,Kafka诞生了(2012年),并提供了一个完美的解决方案。
重要要点:
- 解释消息队列及其优势
- 解释卡夫卡中的组成部分(经纪人,生产者,消费者,消费者组)
- 解释为什么卡夫卡这么快
什么是消息系统?
在了解Kafka之前,如果您不知道什么是Message Queue,则需要添加它。 如果您已经知道,则可以跳到下一段。
> Morden Distributed System
如上图所述,Message Queue是一个在两个系统之间传输和存储消息的中间件。 其外观具有以下优点:
- 去耦:只要您确保双方遵守相同的接口约束,就可以独立扩展或修改双方的处理。
- 冗余:消息队列将数据保留到完成处理为止,从而避免了数据丢失的风险。 在许多消息队列采用的"插入-获取-删除"范式中,从队列中删除消息之前,您的处理系统需要清楚地表明该消息已被处理,以确保您的数据安全保存。 完成使用它。
- 可伸缩性:由于消息队列使您的处理脱钩,因此,只要添加其他处理,就很容易增加消息入队和处理的频率。
- 灵活性和高峰处理能力:在流量急剧增加的情况下,应用程序仍然需要继续发挥作用,但是这种突发流量并不是标准的。 毫无疑问,以能够处理高峰访问为标准来投资资源是巨大的浪费。 消息队列的使用可使关键组件承受突然的访问压力,而不会由于意外的过载请求而完全崩溃。
- 可恢复性:当系统的某些部分发生故障时,它不会影响整个系统。 消息队列减少了进程之间的耦合,因此,即使处理消息的进程挂断了,恢复系统后仍可以处理添加到队列中的消息。
- 顺序保证:在大多数使用情况下,数据处理的顺序至关重要。 大多数消息队列最初都是经过排序的,可以保证数据将按特定顺序进行处理。 (Kafka保证分区中消息的顺序)
- 缓冲:有助于控制和优化通过系统的数据流速度,并解决生产消息和消耗消息的不一致处理速度。
- 异步通信:很多时候,用户不希望也不需要立即处理消息。 消息队列提供了一种异步处理机制,该机制允许用户将消息放入队列,但不能快速处理。 将所需数量的消息放入队列,然后在需要时进行处理。
同时,我认为最大的缺点是复杂性,其优点完全可以忽略不计。
Kafka如何运作?
对于Kafka而言,从独立的角度来看,其中包括生产者,消费者和经纪人。
- 生产者负责将消息发送到代理固定主题
- 代理维护一组主题并管理该主题中的分区
- 消费者,负责从经纪人的相应主题中提取消息
> Kafka components
如图所示,不同的生产者可以将消息发送到多个主题的多个分区,而消费者也可以从各种主题中消费。
生产者和消费者完全孤立。
在此设计中,它充分体现了去耦,灵活性和峰值处理能力,订单保证和异步通信。
Kafka如何在分布式环境中工作?
1. 集群
多个代理和副本。
- 副本,分区副本,以确保分区的高可用性
- 领导者,副本,生产者和使用者中的角色仅与领导者互动
- 追随者中的一个角色,副本以复制领导者中的数据。
Kafka如何保证冗余,可恢复性和高可用性?
即使某些节点发生故障,复制也可以提供高可用性:
- 生产者可以继续发布消息
- 使用者可以继续接收消息。 有两种方案可确保强而一致的数据复制:主备份复制和基于仲裁的复制。 两种方案都需要选举一位领导者,其他人则作为跟随者。 所有写操作都发送给领导者,然后领导者将消息发送给跟随者。
基于仲裁的复制可以使用筏和Paxos等算法,例如Zookeeper,Google Spanner等。在2n +1个节点的情况下,最多可以容忍n个节点故障。
仅在成功接收到消息后,基于主数据库的复制以及其他主数据库和备份的写入操作才能成功。 对于n个节点,最多可以容忍n-1个节点故障,例如Microsoft的PacifiaA。
这两种方法各有优缺点。
- 基于仲裁的延迟可能比主备份更好,因为基于仲裁的方法仅需要一些节点即可成功写入以返回。
- 在相同数量的节点下,基于主备份的复制可以承受更多的节点故障,并且只要一个节点处于活动状态就可以正常工作。
- 在有两个节点的情况下,主备份可以提供容错能力,基于仲裁的方法至少需要三个节点。
Kafka采用第二种方法,即主从模式,该方法主要基于容错能力,并且在两个节点的情况下也可以提供高可用性。
如果节点很慢怎么办?
首先,这种情况很少发生。 如果发生这种情况,您可以设置超时参数来处理这种情况。
Kafka的复制适用于分区。
例如,在上图中,有四个代理,一个主题和两个分区。 复制因子是三。 当生产者发送消息时,它将选择一个分区,例如topic1-part1分区,将消息发送到该分区的领导者,broker2,broker3将拉出消息,消息被拉出后,从属将发送ack到 主机,这次主机仅提交此日志。
在此过程中,生产者有两种选择:
- 一种是等待所有副本被成功提取,然后生产者盘收到成功的响应。
- 另一种是等待领导者成功编写并获得成功的响应。
在第一个中,您可以确保在异常情况下不会丢失消息,但是延迟会减少。 后者的等待时间已大大改善,但是一旦出现异常情况,从属服务器将无法在领导挂起之前提取最新消息。 在这种情况下,可能会丢失该消息。
2. 客户群
消费者使用消费者组名称标记自己,并且发布到主题的每条记录都会传递到每个订阅消费者组中的一个消费者实例。 使用者实例可以在单独的进程中或在单独的机器上。
如果所有使用者实例都具有相同的使用者组,那么将在这些使用者实例上有效地平衡记录。
如果所有使用者实例具有不同的使用者组,则每条记录将广播到所有使用者进程形成正式文件
简而言之,消费者群体是Kafka生态系统中的真正消费者。
3. 控制者
上图是2015年Kafka Controller的设计图。 Controller和ZK共同构建了Kafka的高层架构,该架构主要完成以下任务:
- 管理经纪人和消费者的动态加入和离开。
- 触发负载平衡。 当经纪人或使用者加入或离开时,将触发负载均衡算法,从而为一个使用者组中的多个使用者进行订阅负载均衡。
- 维护每个分区的消耗关系和消耗信息。
为什么Kafka这么快?
Kafka中有一个过程,其中大量网络数据被持久保存到磁盘(生产者到代理),并且磁盘文件通过网络发送(经纪人到消费者)。
此过程的性能直接影响Kafka的整体吞吐量。
1. 零复制
上图的左侧是传统的四个副本和四个上下文切换。
- 首先,通过系统调用将文件数据读入内核状态缓冲区(DMA复制)
- 然后,应用程序将内存状态缓冲区数据读入用户状态缓冲区(CPU副本)
- 接下来,用户程序在通过套接字发送数据时读取用户状态缓冲区数据。复制到内核状态缓冲区(CPU复制)
- 最后,通过DMA复制将数据复制到NIC缓冲区。 同时,它伴随着四个上下文切换。
在上图的右侧,Kafka使用Linux 2.4+内核sendfile系统调用来实现零复制。
- 数据通过DMA复制到内核状态缓冲区
- 它通过DMA直接复制到NIC缓冲区,而无需CPU复制
因为sendfile调用完成了整个文件读取网络的传输,所以整个过程只有两个上下文切换,因此性能大大提高了。
准确地说,Kafka的数据传输是通过TransportLayer完成的,其子类PlaintextTransportLayer通过Java NIO的FileChannel的transferTo和transferFrom方法实现了零复制。
2. 顺序访问
> Compare
上图显示,即使顺序读取磁盘,顺序访问的巨大优势也比基于内存的随机访问要好。
Kafka中的每条消息都会被追加,并且不会从中间写入或删除消息,以确保顺序访问磁盘。
即使是顺序读取和写入,过多的小型IO操作也会导致磁盘瓶颈,并且这次变成了随机读取和写入。
Kafka的策略是汇总消息并分批发送,以最大程度地减少对磁盘的访问。 因此,Kafka的主题和分区的数量不应过多。
通常,经过64个主题/分区之后,Kafka的性能将急剧下降。
3. 段日志
- Kafka使用该主题来管理消息。 每个主题包含多个部分,每个部分对应一个逻辑日志,并且由多个部分组成。
- 多个消息存储在每个段中。 它的逻辑位置决定了消息ID,即消息ID可以直接定位到消息的存储位置,从而避免了ID到位置的附加映射。
- 每个部分对应于内存中的一个索引,并记录每个段中第一条消息的偏移量。
- 发布者发送给特定主题的消息将平均分配到多个部分(随机或根据用户指定的回调函数),代理接收已发布的消息并将消息添加到相应部分的最后一段。 当段上的消息数达到配置的值或消息发布时间超过阈值时,段上的消息将刷新到磁盘,只有刷新到磁盘的消息订阅者才能订阅该消息。 段达到特定大小后,将不再有数据写入该段,代理将创建一个新段。
这种分区分割和索引设计不仅提高了数据读取的效率,而且还提高了数据操作的并行性。
4. 高性能Broker
Kafka在Broker中的设计也是其如此之快的原因之一。
首先,客户端发送的所有请求都将发送到接受器。 代理中将默认有三个线程。 这三个线程称为处理器。
接受者将不会对客户的请求进行任何处理,而是直接对其进行封装。 将socketChannel发送到这些处理器以形成队列。
发送的方法是轮询,即先发送到第一个处理器,然后再发送到第二个,第三个处理器,然后再返回到第一个处理器。 当使用者线程使用这些socketChannel时,它将获取请求请求,并且数据将伴随这些请求请求。
默认情况下,线程池中有八个线程。 这些线程用于处理请求和解析请求。 如果请求是书面请求,则将其写入磁盘。 如果读取,则返回结果。 处理器将从响应中读取响应数据,然后将其返回给客户端。
这是Kafka的三层网络架构。
因此,如果我们需要增强和调整Kafka,增加处理器并增加线程池中的处理线程,则可以达到效果。 考虑到处理器生成请求的速度过快,并且线程数量不足以及时处理请求,因此请求和响应实际上是一种缓存效果。
总结
我希望本文对您了解和初步了解Kafka,它具有的组件以及为什么可以实现如此高的性能有所帮助。
Kafka在现代高并发系统体系结构中扮演着至关重要的角色,并且仍在快速发展,例如流媒体。
本文仅从概念和简单设计原理方面说明Kafka。 仅仅掌握它是不够的。
如果您需要更深入的分析,请参阅官方文档。
谢谢阅读!