用上图的 Topic A 举例, Producer 1 发送消息到 Topic-A ,消息会在存放在 Broker-2 和 Broker-3 的两个分区上,并且由于 Topic-A 开启了分区备份,所以每个分区都会由另外一个节点 Topic-A' 备份分区数据 。发送到 Broker 的数据会被消费者订阅,由于 Consumer-1 和 Consumer-2 在同一个消费者组中,他们只能消费一个固定分区的消息, Consumer-1 只会接收到 Topic-A Partition-1 的消息,Consumer-2 只会接收到 Topic-A Partition-0 的消息。
Broker在 Kafka 集群中的一个 Kafka Server 就是一个 Broker ,生产者将消息投递到 Broker ,Broker 保证消息的 持久化,容灾,准确性等。同时接受消费者的消息订阅,向消费者分发消息。一般来说在生产环境一台 Kafka 服务器就是一个 Broker。
Topic & Partition & LogTopic 可以认为是用来存储消息的逻辑概念,可简单认为他是一个 信箱 。每条消息发送的时候都需要指定需要发送到哪个 Topic ,消息被消费的时候也需要指定消费哪个 Topic 中的消息。
Kafka 为了提高可扩展性以及吞吐量,Topic 被分成多个分区 (Partition) ,每个 Partition 对应一个 Log,Log 是一个逻辑概念, 它会对应服务器上一个文件夹,这个文件夹下存放的是这个 Partition 下所有的消息数据和消息索引 。在面对海量数据的时候,为了避免出现巨大文件出现 I/O 瓶颈,Kafka 又将 Log 分为多个 Segment 。每个 Segment 包含 log 文件 和 index 文件 文件命名是以该 Segment 第一条消息的 offset 命名。这样说下来其实还是很绕的直接看下面的架构图,可以仔细留意一下各个部分的标识和数字再结合这段文字,理解起来应该就很轻松了。
另外因为 Kafka 采用顺序 I/O,顺序 I/O 效率非常高,甚至比随机写内存效率更高,这也是 Kafka 高性能的原因之一。
Replication在生产环境中,我们一般会开启 Kafka 消息冗余特性,每个 Partition 都有 1 个或多个副本,我们称之为 Replication。当分区只有一个副本的时候,该分区数据只保留了一份。每个分区副本都会选出一个 Leader , Leader 是所有读写请求的 “接口人” ,其余副本均为 Follower 。Follower 作用有两个:拉取 Leader 的 Log 数据做 备份 ,在 Leader 失败后作为候选人 参与 Leader 选举 。
Producer消息产出的源头,通过一定的策略推送到 Topic 的各个分区 。这里所说的推送策略就是消息路由机制,Kafka 内置多种策略可选例如:按照消息 Key ,轮训等等,甚至用户可以写扩展代码来自定义路由策略。
Consumer & Consumer Group消费者(Consumer) 主要工作是从 Broker 拉取消息,进行消费处理。每个消费者维护自己的消费进度,这样的设计有诸多好处,比如:每个消费者进度能够轻松的进行区分,并且可以修改单个消费者的消费位点跳过或者重新消费某些消息,避免了位点信息的集中化管理的单点故障问题。
现在的应用程序大部分为分布式的系统,一个应用有几十台上百台服务器,这些服务器上运行着相同的代码,那么一个消息过来,每台服务器都执行一次消费逻辑,岂不是会造成巨大的问题。
所以 Kafka 引入了一个新的概念: 消费者组(Consumer Group) 。我们可以将这些应用的服务器都放到同一个消费者组中,而 Kafka 规定一条消息只能被同一个消费者组中的一个消费者消费,这样就能完美避免分布式情况下的重复消费问题了。上面所说的情况简单来说是希望实现消息被某台服务器独占,也就是 单播 问题。假如我们希望这条消息被广播出去,每台收到这个消息的服务器都做处理,例如发消息做日志清理,这种情况称为 广播 , 那我们只需要将每个消费者放到不同的消费者组即可。
Kafka 引入消费者组的概念巧妙解决了单播和广播问题,而没有区分订阅类型,通过一种逻辑概念来屏蔽掉多种订阅实现。
另外在同一个消费者组中的消费者订阅的分区是确定的,只有在消费者组中的消费者有变化的时候才会进行重分配。例如我们有四个分区,三个消费者,就会出现一个消费者订阅两个分区的情况。而三个分区四个消费者就会出现有消费者处于空闲状态,造成浪费,所以一般消费者的数量尽量不要大于 Topic 的分区数。
尾声(唠叨)