前言

上一篇我们认识了一下Kafka以及完成了一个快速入门程序。这篇文章我们将学习Kafka的整体架构,工作流程,以及一些核心概念,正所谓知其然知其所以然。

Kafka的架构

我们知道,消息队列的工作流程需要三部分组成:Producer生产者 ,Kafka服务器 ,Consumer消费者,生产者发送消息到Kafka,消费者从Kafka拉取消息。而Kafka本身是分布式架构的,如下图:
在这里插入图片描述
看过我《RocketMQ入门经典》的朋友应该能看出来这个图和RocketMQ的架构图是很相似的,或者说RocketMQ很多地方就是参考Kafka设计的。Kafka是分布式+集群的,一个Kafka集群由多个Brocker组成,这里的Brocker可以理解为是集群中的一个机器,相关概念我们往下看

Topic 主题 和 Partition 分区 和 offset 位移

topic : topic主题,消息的逻辑分类,可以理解为消息发送到哪个位置,发送者发送消息需要指定topic,消费者通过订阅topic来消费消息 ,一个topic可被多个消费者订阅,这样可以更快的消费消息。通常使用Topic区分业务,比如:订单topic-order ; 用户:topic-user ;

还有一个概念是Partition分区, 一个topic是由多个partition组成。partition是一个不可修改的消息序列,消息被存储在Partition中,所以Kafka 采用了 topic-partition-message 三级结构来处理消息。partition 上的每条消息都会被分配 个唯一的序列号,该序列号被称为位移(offset),从0开始递增,他用来确定该从哪个位置追加消息内容。
在这里插入图片描述

另外消费端也是需要通过位移来确定该消费哪一条消息,只不过消费端的位移和生产端的位移并不是同一个概念。消费端的位移会随着消息被消费不停的后移,直到消费完最后一条消息
在这里插入图片描述
所以,如果要从Kafka集群中确定某条消息的位置应该是根据 topic ,partition , offset 的顺序来找。

replica 副本(leader 和 follower)

生产者的消息发送到Kafka,Kafka把消息顺序存储到磁盘上,如果一旦Kafka服务器挂掉,很有可能会导致数据丢失,Kafka使用冗余机制也就是把数据进行备份的方式来达到消息可靠性。这里的数据备份在kafka中被叫做 replica副本,就是为了防止消息丢失而存在的。

replica分为 leader replica领导者副本以及follower replica 追随者副本,leader负责写入消息和消费消息(给生产者,消费者提供服务),follower不负责写入消息和消费消息,他只是被动的向leader同步数据以达到数据备份的目的,如果leader所在的brocker挂掉,Kafka 会从剩余的 replica 中选举出新的 leader 续提供服务,因此为了容错,某leader的follower是不会分配到一台brocker机器上的

Kafka为partition动态维护了一个replica集合,集合中的所有replica都要和leader replica保持数据同步,当leader挂掉,就从该集合中选举一个新的leader 。生产在把消息写入到leader,所有的replica副本都收到该消息,kafka才会把消息状态标记为“已提交”,也就是消息发送成功。如果当某个replica的消息同步进度落后于leader, kafka会把这些replica “踢出”,当replica追上进度又会被重新加入。

kafka的吞吐量

Kafka多用于大数据处理,这得益于它的高吞吐量低延迟,那么它是如何做到高吞吐量、低延时的呢?Kafka的数据会儿比持久化到磁盘,但是每次写入数据的时候是写入到操作系统的页缓存(page cache)中,然后由操作系统把缓存中的数据库写回磁盘。这就体现了如下几个优势

  • 统页缓存是在内存中分配的,消息写入的速度非常快。
  • Kafka 不必直接与底层的文件系统打交道。所有烦琐的 IO操作都交由操作系统来处理
  • Kafka 写入操作采用追加写入( append )的方式,避免了磁盘随机写操作。

这里我们注意一点,通常我们认为写磁盘比写内存速度慢,但是如果是磁盘的顺序读写是非常快的,可以匹敌内存的随机读写。Kafka 在设计时采用了追加写入消息的方式,即只能在日志文件末尾追加写入新的消息,且不允许修改己写入的消息,因此它属于典型的磁盘顺序访问型操作,所以Kafka 消息发送的吞吐量是很高的。在实际使用过程中可以很轻松地做到每秒写入几万甚至几十万条消息。

既然Kafka是先把数据写入 操作系统的页缓存,那么Kafka在读数据的时候也会先从页缓存读取,然后把消息发送到网络的Socket,这个过程是就是使用的是sendfile零拷贝技术。其数据的读取速度是非常快的。

另外, Kafka 实现持久化的设计也有新颖之处。普通的系统在实现持久化时可能会先尽量使用内存,当内存资源耗尽时,再 次性地把数据“刷盘”;而 Kafka 则反其道而行之,有数据都会立即被写入文件系统的持久化日志中,之后 Kafka 服务器才会返回结果给客户端通知它们消息已被成功写入。这样做既实时保存了数据,又减少了 Kafka 程序对于内存的消耗,从而将节省出的内存留给页缓存使用,更进一步地提升了整体性能

所以,Kafka之所以拥有这么高的吞吐量和低延迟有这么些原因:

  • 大量使用操作系统页缓存,内存操作速度快且命中率高。
  • Kafka 写数据不直接参与物理 IO 操作,而是写到系统缓存,交由最擅长此事的操作系统来完成数据入盘。
  • 采用顺序追加写入方式,摒弃了缓慢的磁盘随机读/写操作。
  • 使用以 sendfile 为代表的零拷贝技术加强网络间的数据传输效率。

负载均衡

Kafka集群是由多个brocker机器组成,那就意味着客户端的读写请求会按照一定的规则分担到各个brocker上,以这样的方式来提高Kafka整体性能,也是防止单个brocker资源耗尽而宕机的风险。

另外我们上面提到 partition 和 replica 的概念,为了实现数据的可靠性,kafka会把partition 的 leader 平均分配在各个brocker及其上,整体上实现负载均衡。
在这里插入图片描述

故障转移

故障转移是指当 集群检测到某个服务器故障,可以快速的把当前服务器上的应用或者服务转移到另一台可用的服务器上,在Kafka中,每台服务器都会注册到Zookeeper服务器上,一旦服务器故障,与Zookeeper的会话就无法保持,集群就会选举出另一台服务器来代替故障的服务器继续服务。

伸缩性

Kafka集群中的每台机器都注册到Zookeeper,由Zookeeper统一保存机器状态,如果要扩容,也只需要增加新的Kafka服务器,注册到Zookeeper中即可。在分布式领域这种思想使用的非常多,SpringCloud的Eureka, Nacos ,RocketMQ的NameServer都是如此。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐