Kafka 学习
Kafka
组件
- Producer:生产者,负责生产消息并发送到 kafka。
- Consumer:消费者,连接到 kafka 上并接收消息
- Consumer group:消费者组,包含多个消费者实例。不同 group 下的消费者可以重复消费数据,同一 group 下的消费者,消息只能被其中一个消费者消费。
- Broker:kafka 的服务节点,即 kafka 的服务器
- Topic:kafka 中的消息按照 topic 进行区分,生产者将消息发送到特定 topic,消费者订阅 topic 的消息并进行消费
- Partition:一个 topic 可被分为多个 partition,同一个主题下不同 partition 包含的信息是不同的。(存储层面上可看作一个可追加的 log 文件,消息被追加到文件后会被分配一个 offset 偏移量)
- Offset:消息在分区中的唯一标识,不跨分区。每个 consumer 自己管理 offset,控制消费的信息
- Replication:副本,kafka 中一个 partition 会在多 broker 上存在多个副本,只有主副本提供对外读写,从副本实时同步主副本的数据。主副本所在 broker 出现故障时,controller 会选举新的主副本对外提供读写服务,保证高可用。
- Record:实际写入 kafka 的消息记录,每个 record 包含 key,value 和 timestamp
Zookeeper
Kafka 时使用 Zk 构建的分布式系统,Zk 负责了
- Kafka Controller 的 Leader 选举
- Kafka 集群成员管理
- Topic 配置管理
- 分区副本管理
Controller
从 broker 中选举出来,负责分区 leader 和 follower 的管理
选举过程:
Broker 启动的时候尝试去读取 /controller
节点的 brokerid 的值,如果 brokerid 的值不等于-1,则表明已经有其他的 Broker 成功成为 Controller 节点,当前 Broker 主动放弃竞选;如果不存在 /controller
节点,或者 brokerid 数值异常,当前 Broker 尝试去创建 /controller
这个节点,此时也有可能其他 broker 同时去尝试创建这个节点,只有创建成功的那个 broker 才会成为控制器,而创建失败的 broker 则表示竞选失败。每个 broker 都会在内存中保存当前控制器的 brokerid 值,这个值可以标识为 activeControllerId。
实现:
Controller 内部也采用生产者-消费者实现模式,Controller 将 zookeeper 的变动通过事件的方式发送给事件队列,队列就是一个LinkedBlockingQueue,事件消费者线程组消费事件,将相应的事件同步到各 Broker 节点。这种队列 FIFO 的模式保证了消息的有序性。
职责:
- 处理 broker 节点的上线、下线,更新集群元数据,并将集群变化通知到所有 broker 节点
- 负责分区副本的分配工作,主导 topic 分区主副本的选取,创建 topic 或者 topic 扩容分区
- 管理分区和副本的状态机
分区状态机
PartitionStateChange,管理 Topic 的分区,它有以下 4 种状态:
- NonExistentPartition:该状态表示分区没有被创建过或创建后被删除了。
- NewPartition:分区刚创建后,处于这个状态。此状态下分区已经分配了副本,但是还没有选举 leader,也没有 ISR 列表。
- OnlinePartition:一旦这个分区的 leader 被选举出来,将处于这个状态。
- OfflinePartition:当分区的 leader 宕机,转移到这个状态。
副本状态机
- NewReplica: 创建 topic 和分区分配后创建 replicas,此时,replica 只能获取到成为 follower 状态变化请求。
- OnlineReplica: 当 replica 成为 parition 的 assingned replicas 时,其状态变为 OnlineReplica, 即一个有效的 OnlineReplica。
- OfflineReplica: 当一个 replica 下线,进入此状态,这一般发生在 broker 宕机的情况下;
- NonExistentReplica: Replica 成功删除后,replica 进入 NonExistentReplica 状态。
Kafka 性能
IO
Kafka 文件采取顺序写的方式,减少了磁盘寻道和旋转的次数,减少了磁盘 IO 的时间
零拷贝
传统数据从磁盘到网卡需要经过四次拷贝和两次系统调用:
- 操作系统从磁盘读取数据到内核空间的 pagecache
- 应用程序读取内核空间的数据到用户空间的缓冲区
- 应用程序将数据(用户空间的缓冲区)写回内核空间到套接字缓冲区(内核空间)
- 操作系统将数据从套接字缓冲区(内核空间)复制到通过网络发送的 NIC 缓冲区(网卡缓冲区)
零拷贝省去中间步骤,只用将磁盘文件的数据复制到页面缓存中一次,然后将数据从页面缓存直接发送到网络中(发送给不同的订阅者时,都可以使用同一个页面缓存),避免了重复复制操作。
如果有10个消费者,传统方式下,数据复制次数为4*10=40次, 而使用“零拷贝技术”只需要1+10=11次,一次为从磁盘复制到页面缓存, 10次表示10个消费者各自读取一次页面缓存。
生产者生产的消息会先写入 page cache,之后会写入到磁盘。leader 和 follower 的同步,与 consumer 消费信息的原理是一样的。因此如果 Kafka producer 的生产速率与 consumer 的消费速率相差不大,那么就能几乎只靠对 broker page cache 的读写完成整个生产 - 消费过程,磁盘访问非常少。
Kafka 的 Consumer 客户端是线程不安全的,在 Consumer 客户端采用 Reactor 线程模型可以保证线程安全,提升消费性能