Kafka 是一个分布式消息队列,具有高性能、持久化、多副本备份、横向扩展能力。生产者往队列里写消息,消费者从队列里取消息进行业务逻辑。一般在架构设计中起到解耦、削峰、异步处理的作用。
Kafka 对外使用 Topic 的概念,生产者往 Topic 里写消息,消费者从中读消息。
为了做到水平扩展,一个 Topic 实际是由多个 Partition 组成的,遇到瓶颈时,可以通过增加 Partition 的数量来进行横向扩容。单个 Parition 内是保证消息有序。
每新写一条消息,Kafka 就是在对应的文件 append 写,所以性能非常高。
Kafka 的总体数据流是这样的:
大概用法就是,Producers 往 Brokers 里面的指定 Topic 中写消息,Consumers 从 Brokers 里面拉取指定 Topic 的消息,然后进行业务处理。
图中有两个 Topic,Topic0 有两个 Partition,Topic1 有一个 Partition,三副本备份。
可以看到 Consumer Gourp1 中的 Consumer2 没有分到 Partition 处理,这是有可能出现的,下面会讲到。
关于 Broker、Topics、Partitions 的一些元信息用 ZK 来存,监控和路由啥的也都会用到 ZK。
生产
基本流程是这样的:
创建一条记录,记录中一个要指定对应的 Topic 和 Value,Key 和 Partition 可选。
先序列化,然后按照 Topic 和 Partition,放进对应的发送队列中。Kafka Produce 都是批量请求,会积攒一批,然后一起发送,不是调 send() 就立刻进行网络发包。
如果 Partition 没填,那么情况会是这样的:
-
Key 有填。按照 Key 进行哈希,相同 Key 去一个 Partition。(如果扩展了 Partition 的数量那么就不能保证了)
-
Key 没填。Round-Robin 来选 Partition。
这些要发往同一个 Partition 的请求按照配置,攒一波,然后由一个单独的线程一次性发过去。
API
有 High Level API,替我们把很多事情都干了,Offset,路由啥都替我们干了,用起来很简单。
还有 Simple API,Offset 啥的都是要我们自己记录。(注:消息消费的时候,首先要知道去哪消费,这就是路由,消费完之后,要记录消费单哪,就是 Offset)
Partition
当存在多副本的情况下,会尽量把多个副本,分配到不同的 Broker 上。
Kafka 会为 Partition 选出一个 Leader,之后所有该 Partition 的请求,实际操作的都是 Leader,然后再同步到其他的 Follower。
当一个 Broker 歇菜后,所有 Leader 在该 Broker 上的 Partition 都会重新选举,选出一个 Leader。(这里不像分布式文件存储系统那样会自动进行复制保持副本数)
然后这里就涉及两个细节:
-
怎么分配 Partition
-
怎么选 Leader
关于 Partition 的分配,还有 Leader 的选举,总得有个执行者。在 Kafka 中,这个执行者就叫 Controller。
Kafka 使用 ZK 在 Broker 中选出一个 Controller,用于 Partition 分配和 Leader 选举。
Partition 的分配:
-
将所有 Broker(假设共 n 个 Broker)和待分配的 Partition 排序。
-
将第 i 个 Partition 分配到第(i mod n)个 Broker 上 (这个就是 Leader)。
-
将第 i 个 Partition 的第 j 个 Replica 分配到第((i + j) mode n)个 Broker 上。
Leader 容灾
Controller 会在 ZK 的 /brokers/ids 节点上注册 Watch,一旦有 Broker 宕机,它就能知道。
当 Broker 宕机后,Controller 就会给受到影响的 Partition 选出新 Leader。
Controller 从 ZK 的 /brokers/topics/[topic]/partitions/[par