PinkHello
做一个快乐的程序猿
11 几个关于kafka的知识点

认识kafka

Kafka 是分布式消息系统, Apache 的子项目。标语也变了"分布式流平台", 与传统的消息系统不同点在于

  • 分布式的,易于扩展
  • 为发布和订阅提供了高吞吐
  • 支持多订阅者,在失败的时候能自动平衡消费者
  • 消息的持久化

kafka 的架构

几点?

  • KafkaTopicPartition 内部如何存储?
  • 与传统的消息系统相比, Kafka 消费模型有啥优点?
  • Kafka 是如何实现分布式数据存储和数据的读取?

Kafka 架构

一个 Kafka 集群,多个 Producer ,多个 Consumer ,多个 Broker , 选举 Leader 以及在 Consumer Group 发生变化时进行 reblance

  • Broker 消息中间件的处理节点,一个 Kafka 节点就是一个 Broker , 一个或者多个 Broker 组成 Kafka 集群
  • Topic Kafka 根据 TopicMessage 进行归类,发布到 Kafka 的每条 Message 都要指定 Topic
  • ProducerBroker 发生 message
  • ConsumerBroker 读取 message
  • Consumer Group 每个 Consumer 属于特定的 Group,一个 Message 可以发送给不同的 Consumer Group ,但是同一个 Group 下的只有一个 Consumer 能消费该 Message
  • Partition 物理概念,一个 Topic 下可以分为多个 Partition, 每个 Partition 下是有序的。

kafka 框架图 kafka 数据流向

下面来讲述 上面为问题啊

KafkaTopicPartition 内部如何存储?

Kafka 为每个 Topic 维护了分区( Partition )的日志文件,每个 Partitionkafka 存储层为 Append Log。任何发布到此 Partition 的消息都会被追加到 Log 文件尾部。 在每个 Partition 每个消息是按照 Timeline 分配到一个单调递增的顺序编号的,就是我们说的 Offset, Offset 是一个 long 型的数字,也是我们可以通过这个 Offset 确定一条在该 Partition 下的唯一消息。

这样就有个特性: Partition 下有序, Topic 下无法保证有序。

那么,问题来了?这些 Message 如何发送到各个 Partition 的呢?如何指定的呢?

  • 发送至哪个 Partition 是由 生产者决定的。
  • 如果没有 Key 值,则轮询发送
  • 如果有 key 值,对 key 值进行 Hash ,然后对分区数量取余,保证同一个 Key 值的会路由到同一个 Partition 。如果想队列强顺序一致,可以让所有的消息设置为同一个 Key

与传统的消息系统相比, Kafka 消费模型有啥优点?

Kafka Consumer 消费模型

消息由 producer 发送到 Kafka 集群后,被 Consumer 消费,一般来说我们的消费模型有两种: Push 推送模型 和 Pull 拉取模型

Push 推送模型:

消息 Broker 记录消费状态。消息 Broker 将消息推送给消费者后,记录这条消息已经被消费,但是这种方式无法很好的保证消费处理的语义。 消息推送完成后,消费者挂掉或者线程 Hang 住 ,或者网络原因未收到,但是 消息 Broker 将其标记为已消费,这个消息将永远丢失了。 也就是说,采用 Push ,消息消费完全依赖 消息 Broker 控制,一旦 Consumer 发生阻塞,会出现问题。

Pull 拉取模型:

Consumer 控制 Speed ,以及消费 OffsetConsumer 可以按照任意的偏移量进行 Consumer Consumer 可以回放已经消费过的消息,进行重新处理或者消费最近的消息

Kafka 的网络模型

Kafka Client 单线程 Selector

kafka 单线程模型

并发链接数目小,逻辑简单,数据量小。在 Kafka ConsumerKafka Producer 都是采用的 单线程模型。

kafka Server 多线程 Selector

kafka 多线程模型

Acceptor 运行于一个单独的线程,对于读取操作的线程池中线程都是在 selector 注册 Op_Read 事件,负责服务端读取请求。 成功读取后,将请求放入 Message Queue 共享队列中,然后在 写操作的线程池中,取出这个请求,对其进行逻辑处理。 即使某个线程阻塞了,后面还有后续的线程从 Message Queue 共享队列里面获取请求进行处理。 写线程处理完逻辑后,由于注册了 Op_Write 事件,还需要发送响应。

Kafka 是如何实现分布式数据存储和数据的读取?

Kafka 高可靠的分布式存储模型

主要依靠 副本 机制,有了副本机制,机器宕机,也会恢复。

Kafka 高性能日志存储

kafka 高性能日志存储 kafka 高性能日志存储

Kafka 的一个 Topic 下的所有的 Message 都是以 Partition 的方式,分布式存储在多个节点上。

同时在 kafka 机器上,每个 Partition 都会对应一个日志目录,在目录下面会对应对歌 日志分段(LogSegment)。 LogSegment 组成 “.index” 与 “.log” , 分别表示 segment 索引文件 和 数据文件。 两个文件的命名规则: partition 全局的第一个 segment 从 0 开始,后续的每个 Segment 文件名为上一个 Segment 文件最后一条消息的 offset 值。 数值为 64 位, 20 位数字字符长度,没有数字用 0 填充。

假如有 1000 条消息,每个 LogSegment 的大小为 100,那么展现 900-1000 的索引 和 Log: kafka LogSegment

kafka 消息数据量打,采用的是 稀疏索引 的方式,加快偏查询速度。

如何读取数据? 如果我们要读取第 911 条数据

  • 先找到它属于哪个段? 二分法查找属于文件,找到 0000900.index 和 0000900.log 之后
  • 去索引文件 0000900.index 查找 (911-900)=11 这个索引 或者 小于11 最近的索引
  • 后面通过 二分法我们找到索引 [10,1367]
  • 然后通过这条索引的物理位置 1367,开始往后找,知道找到 第 911 条数据

为什么分区?只有一个分区不行么?分区是为了干啥?那么日志为什么要分段呢?

Kafka 的副本机制

Kafka 副本机制为 多个节点对其他服务端节点的主题分区的日志进行复制。当集群某个节点出现问题,访问该故障节点的请求可以被转移到其他正常的节点(过程脚 reblance

kafka 的每个 Topic 的每个 Partition 都有一个 主副本以及 0-n 个副本,副本保持与主副本的数据同步,当 主副本 出现故障时会被替代。

kafka 高性能副本机制

kafka 中,并不是所有的副本都被拿来代替主副本的,所以在 kafkaleader 节点中维护者一个 ISR(In Sync Replicas)集合 ISR (In Sync Replicas) 副本 follower 同步队列, 维护着有资格的 follower 的节点

  • 副本的所有节点必须和 ZK 保持链接
  • 在同步过程中,这个副本不能落后主副本太多(即副本最后一条消息的 offsetleader 副本的最好一条消息的 offset 之间的差值不能超过阈值) (replica.lag.max.messages)

AR(Assigned Replicas)标记副本的全集 ,OSR 表示落后被剔除的副本集合

ISR = leader + 没有落后太多的副本 AR = OSR + ISR

HW & LEO Follower 副本同步过程中,两个概念 HW HighWatermarkLEO Log End Offset,与 ISR 紧密相关。

HW 是一个特殊的 Offset ,当 Consumer 处理消息的时候,只能 PullHW 之前的消息, HW 之后的消息对 Consumer 不可见。 也就是说 Partition 对应的 ISR 中最小的 LEO 作为 HWConsumer 最多只能消费到 HW 所在的位置,每个 Replica 都有 HWleaderfollower 各自维护更新自己的 HW 的状态,对于 Leader 新写入的消息, Consumer 不能立刻消费, Leader 会等待 该消息被所有的 ISR 中的 Replicas 同步更新 HW ,此时消息才能被 Consumer 消费,这样保证了如果 Leader 副本损坏, 该消息仍然可以从新选举的 Leader 获取。

LEO 是所有副都会有的一个 offset 标记,它指向追加到当前副本的最后一个消息的 offset ,当生产者向 Leader 副本追加消息时候, Leader 副本的 LEO 标记就会递增; 当 follower 副本成功从 leader 副本 pull 消息并更新到本地的时候, follower 副本也会增肌

producerleader 发送消息,可以通过 request.required.acks 参数来设置数据可靠性级别:

  • 1 默认, producerISR 中的 leader 已成功收到数据并得到确认后发送下一条 Message 。如果 Leader 宕机,会丢失数据
  • 0 producer 无需等待,继续发送下一批消息,效率高,但是数据可靠性低
  • -1 producer 需要等待 ISR 中所以的 follower 都确认接收到数据才算发送完成。可靠性高,但是没有也不能保证数据不丢失,比如 ISR 里只有 leader (其他节点和 ZK 断链,或者没有追上),这样就变成了 acks=1 的情况

kafka 到底会不会丢消息

一个消息的流转

  • Producer 发送给 Kafka Broker
  • kafka Broker 消息同步和持久化
  • Kafka Brokder 将消息传递给消费者

如何优雅的使用 Kafka Consumer

注意点等等…..

kafka Producer 流程详细

kafkaProducer流程解读 kafka 获取元数据


最后修改于 2021-05-02