一文带你搞懂 Kafka 的系统架构(深度好文,值得收藏)
Kafka 简介
Kafka 是一种高吞吐、分布式、基于发布和订阅模型的消息系统,最初是由 LinkedIn 公司采用 Scala 和 java 开发的开源流处理软件平台,目前是 Apache 的开源项目。
Kafka 用于离线和在线消息的消费,将消息数据按顺序保存在磁盘上,并在集群内以副本的形式存储以防止数据丢失。Kafka 可以依赖 ZooKeeper 进行集群管理,并且受到越来越多的分布式处理系统的青睐,比如 Storm、Spark、Flink 等都支持与 Kafka 集成,用于实时流式计算。
Kafka 开发者 Jay Kreps 提及关于 Kafka 名字的由来:
“ 因为 Kafka 系统的写性能很强,所以找一个作家的名字来命名似乎是个好主意,大学期间我上了很多文学课,非常喜欢 Franz Kafka 这个作家,另外,这个名字听上去也很酷~~ ”
消息队列是干什么的?
学习 Kafka 不可避免地要认识下消息队列,也就是我们常提到的 MQ(Message Queue),因为 Kafka 本质上也是一个消息队列。
那么消息队列又是什么呢?先来看一个比较官方的回答。
消息队列是一种进程间通信或者同一个进程中不同线程间的通信方式,主要解决异步处理、应用耦合、流量消峰等问题,实现高性能、高可用、可伸缩和最终一致性架构,是大型分布式系统不可缺少的中间件。
再说的直观点,如下图,系统 A 将消息发布到 MQ,然后系统 B 再从 MQ 取消息。
那么,为什么系统 A 不直接发消息给系统 B ,中间还要隔一个 MQ 呢?这就要看下 MQ 的三个主要功能了。
1)异步处理
消息队列提供了异步处理机制,因为很多时候用户并不需要立即响应来处理消息,那么通过这个机制就可以把所有消息放入 MQ 中。比如,某系统发来的数据中包含很多图片信息,如果对其中的信息都进行保存处理,用户一番操作下来可能会很久。采用异步处理之后,系统会将所有数据存放在 MQ 中,用户不需要立即处理,大大缩短了系统的响应时间。
2)应用解耦
消息队列可以对系统间的依赖进行解耦,降低依赖系统变更带来的影响。比如,用户在下单后,订单系统A需要通知系统B、系统C等做出响应的处理。传统的做法,如下图所示。
此时的系统A是强依赖系统B和系统C的,一旦系统B出现故障或者需要重新加入高耦合的系统D时,就必须要更改系统A的代码。
如果经常出现这种依赖系统迭代的情况,那么系统A就会很难维护,可以通过消息队列对依赖系统进行解耦(如下图),这样系统A也无需关心其他系统的可用性。
3)流量削峰
流量削峰还有个形象的名字叫做削峰填谷,其实就是指当数据量激增时,能够有效地隔离上下游业务,将上游突增的流量缓存起来,真正地填到谷中,以平滑的方式传到下游系统,避免了流量的不规则冲击。
比如,有个活动页面平时也就 50qps,某一特殊时刻访问量突然增多,能达到 1000qps,但是当前系统的处理能力最多为 100qps,这个时候可以通过消息队列来进行削峰填谷,如下图所示。
当然,Kafka 除了以上 MQ 这些功能之外,还提供了消息顺序性保障、回溯消息、持久化存储等功能,这个在后续文章中会详细讲解。
MQ 的两种传输模式
消息在 MQ 中有两种传输模型,分别是点对点(point to point)和发布/订阅(publish/subscribe)模型。
1)点对点模型
如图所示,系统A发送的消息只能被系统B接收,其他的任何系统都不能获取到系统A发送的消息。在日常生活中就像A拨通了B的电话,其他人是不可能接听到的。
2)发布/订阅模型
与点对点模型的区别在于发布/订阅模型多了一个 topic 的概念,可以存在多个发布者向相同主题发送消息,而订阅者也可以存在多个,接收相同主题的消息。在日常生活中就像不同主题的报纸期刊,同时也有不同群体的读者来订阅。
那么 Kafka 属于哪种呢,事实上 Kafka 可以同时支持这两种传输模型,这个后面会讲。
Kafka 系统架构
终于到主角登场了,一个典型的Kafka 系统架构会包括 Producer、broker、Cosumer 等角色,以及一个 ZooKeeper 集群,先上个图。
- Producer:生产者,负责将客户端生产的消息发送到 Kafka 中,可以支持消息的异步发送和批量发送;
- broker:服务代理节点,Kafka 集群中的一台服务器就是一个 broker,可以水平无限扩展,同一个 Topic 的消息可以分布在多个 broker 中;
- Consumer:消费者,通过连接到 Kafka 上来接收消息,用于相应的业务逻辑处理。
- ZooKeeper:不废话了~不认识它的可以翻下我前面发的文章,一文搞定 ZooKeeper;
- Consumer Group:消费者组,指的是多个消费者共同组成一个组来消费一个 Topic 中的消息。
前面提到 Kafka 同时支持两种消息传输模型,其中实现点对点模型的方式就是引入了 Consumer Group,目的主要是让多个消费者同时消费,可以加速整个消费者端的吞吐量。
需要注意的是:一个 Topic 中的一个分区只能被同一个 Consumer Group 中的一个消费者消费,其他消费者不能进行消费。这里的一个消费者,指的是运行消费者应用的进程,也可以是一个线程。
在整个 Kafka 集群中 Producer 将消息发送给 broker,然后 broker 再将接收到的消息存储到磁盘中,然后 Consumer 再从 Broker 订阅并消费消息。ZooKeeper 则是 Kafka 集群用来负责集群元数据的管理、控制器的选举等操作的。
Kafka 中的重要概念
1)Topic 与 Partition
在 Kafka 中消息是以 Topic 为单位进行归类的,Topic 在逻辑上可以被认为是一个 Queue,Producer 生产的每一条消息都必须指定一个 Topic,然后 Consumer 会根据订阅的 Topic 到对应的 broker 上去拉取消息。
为了提升整个集群的吞吐量,Topic 在物理上还可以细分多个分区,一个分区在磁盘上对应一个文件夹。由于一个分区只属于一个主题,很多时候也会被叫做主题分区(Topic-Partition)。
2)Leader 和 Follower
一个分区会有多个副本,副本之间是一主(Leader)多从(Follower)的关系,Leader 对外提供服务,这里的对外指的是与客户端程序进行交互,而 Follower 只是被动地同步 Leader 而已,不能与外界进行交互。
当然了,你可能知道在很多其他系统中 Follower 是可以对外提供服务的,比如 MySQL 的从库是可以处理读操作的,但是在 Kafka 中 Follower 只负责消息同步,不会对外提供服务。
一个有意思的事情是现在已经不提倡使用Master-Slave来指代这种主从关系了,毕竟Slave有奴隶的意思,在美国这种严禁种族歧视的国度,这种表述有点政治不正确了,所以目前大部分的系统都改成Leader-Follower了。
Kafka 多副本机制
Kafka 为分区引入了多副本机制,同一分区的不同副本中保存的信息是相同的,通过多副本机制实现了故障的自动转移,当集群中某个 broker 失效时仍然能保证服务可用,可以提升容灾能力。
如图所示,Kafka 集群中有4个 broker,某个 Topic 有三个分区,假设副本因子也有设置为3,那么每个分区就会有一个 Leader 和两个 Follower 副本。
副本处于不同 broker 中,生产者与消费者只和 Leader 副本进行交互,而 Follower 副本只负责消息的同步。当 Leader 副本出现故障时,会从 Follower 副本中重新选举新的 Leader 副本对外提供服务。
接下来我们来了解 Kafka 多副本机制中的一些重要术语。
- AR(Assigned Replicas):一个分区中的所有副本统称为 AR;
- ISR(In-Sync Replicas):Leader 副本和所有保持一定程度同步的 Follower 副本(包括 Leader 本身)组成 ISR;
- OSR(Out-of-Sync Raplicas):与 ISR 相反,没有与 Leader 副本保持一定程度同步的所有Follower 副本组成OSR;
首先,生产者会将消息发送给 Leader 副本,然后 Follower 副本才能从 Leader 中拉取消息进行同步,在同一时刻,所有副本中的消息不完全相同,也就是说同步期间,Follower 相对于 Leader 而言会有一定程度上的滞后,当然这个滞后程度是可以通过参数来配置的。
那么,我们就可以厘清了它们三者的关系:AR = ISR + OSR。
Leader 负责维护和跟踪 ISR 集合中所有 Follower 副本的滞后状态,当 Follower 出现滞后太多或者失效时,Leader 将会把它从 ISR 集合中剔除。
当然,如果 OSR 集合中有 Follower 同步范围追上了 Leader,那么 Leader 也会把它从 OSR 集合中转移至 ISR 集合。
一般情况下,当 Leader 发送故障或失效时,只有 ISR 集合中的 Follower 才有资格被选举为新的 Leader,而 OSR 集合中的 Follower 则没有这个机会(不过可以修改参数配置来改变)。