Kafka机制问题

1 Kafka生产过程分析

1.1 写入方式

  • producer 采用推(push) 模式将消息发布到 broker,每条消息都被追加(append) 到分区(patition) 中,属于顺序写磁盘(顺序写磁盘效率比随机写内存要高,保障 kafka 吞吐率)。

1.2 分区(Partition)

  • Kafka 集群有多个消息代理服务器(broker-server)组成,发布到 Kafka 集群的每条消息都有一个类别,用主题(topic)来表示。通常,不同应用产生不同类型的数据,可以设置不同的主题。一个主题一般会有多个消息的订阅者,当生产者发布消息到某个主题时,订阅了这个主题的消费者都可以接收到生成者写入的新消息。
  • Kafka 集群为每个主题维护了分布式的分区(partition)日志文件,物理意义上可以把主题(topic)看作进行了分区的日志文件(partition log)。主题的每个分区都是一个有序的、不可变的记录序列,新的消息会不断追加到日志中。分区中的每条消息都会按照时间顺序分配到一个单调递增的顺序编号,叫做偏移量(offset),这个偏移量能够唯一地定位当前分区中的每一条消息。
  • 消息发送时都被发送到一个 topic,其本质就是一个目录,而 topic 是由一些 PartitionLogs(分区日志)组成, 其组织结构如下图所示:
    1. 下图中的 topic 有 3 个分区,每个分区的偏移量都从 0 开始,不同分区之间的偏移量都是独立的,不会相互影响
      在这里插入图片描述
      在这里插入图片描述
    2. 我们可以看到,每个 Partition 中的消息都是有序的,生产的消息被不断追加到 Partitionlog 上,其中的每一个消息都被赋予了一个唯一的 offset 值。
    3. 发布到 Kafka 主题的每条消息包括键值和时间戳。消息到达服务器端的指定分区后,都会分配到一个自增的偏移量。原始的消息内容和分配的偏移量以及其他一些元数据信息最后都会存储到分区日志文件中。消息的键也可以不用设置,这种情况下消息会均衡地分布到不同的分区。
  • 分区的原因
    1. 方便在集群中扩展,每个 Partition 可以通过调整以适应它所在的机器,而一个 topic又可以有多个 Partition 组成,因此整个集群就可以适应任意大小的数据了;
    2. 可以提高并发,因为可以以 Partition 为单位读写了。
  • 传统消息系统在服务端保持消息的顺序,如果有多个消费者消费同一个消息队列,服务端会以消费存储的顺序依次发送给消费者。 但由于消息是异步发送给消费者的,消息到达消费者的顺序可能是无序的,这就意味着在并行消费时,传统消息系统无法很好地保证消息被顺序处理。虽然我们可以设置一个专用的消费者只消费一个队列,以此来解决消息顺序的问题,但是这就使得消费处理无法真正执行。
  • Kafka 比传统消息系统有更强的顺序性保证,它使用主题的分区作为消息处理的并行单元。 Kafka 以分区作为最小的粒度,将每个分区分配给消费者组中不同的而且是唯一的消费者,并确保一个分区只属于一个消费者,即这个消费者就是这个分区的唯一读取线程。那么,只要分区的消息是有序的,消费者处理的消息顺序就有保证。每个主题有多个分区,不同的消费者处理不同的分区,所以 Kafka 不仅保证了消息的有序性,也做到了消费者的负载均衡。
  • 分区的原则
    1. 指定了 patition,则直接使用;
    2. 未指定 patition 但指定 key,通过对 key 的 value 进行 hash 出一个 patition
    3. patition 和 key 都未指定,使用轮询选出一个 patition。

1.3 副本

  • 同 一 个 partition 可 能 会 有 多 个 replication ( 对 应 server.properties 配 置 中的default.replication.factor=N)。没有 replication 的情况下,一旦 broker 宕机,其上所有patition 的数据都不可被消费,同时 producer 也不能再将数据存于其上的 patition。引入replication 之后,同一个 partition 可能会有多个 replication,而这时需要在这些 replication之间选出一个 leader, producer 和 consumer 只与这个 leader 交互,其它 replication 作为follower 从 leader 中复制数据

1.4 写入流程

  • producer 写入消息流程如下:
  • 在这里插入图片描述
    1. producer 先从 zookeeper 的 "/brokers/…/state"节点找到该 partition 的 leader
    2. producer 将消息发送给该 leader
    3. leader 将消息写入本地 log
    4. followers 从 leader pull 消息,写入本地 log 后向 leader 发送 ACK
    5. leader 收到所有 ISR 中的 replication 的 ACK 后,增加 HW(high watermark,最后 commit的 offset)并向 producer 发送 ACK

2 Broker 保存消息

2.1 存储方式

  • 物理上把 topic 分成一个或多个 patition(对应 server.properties 中的 num.partitions=3配置),每个 patition 物理上对应一个文件夹(该文件夹存储该 patition 的所有消息和索引文件)。

2.2 存储策略

  • 无论消息是否被消费, kafka 都会保留所有消息。有两种策略可以删除旧数据:
    1. 基于时间: log.retention.hours=168
    2. 基于大小: log.retention.bytes=1073741824
  • 需要注意的是,因为 Kafka 读取特定消息的时间复杂度为 O(1),即与文件大小无关,所以这里删除过期文件与提高 Kafka 性能无关。

2.3 Zookeeper 存储结构

在这里插入图片描述

  • 注意: producer 不在 zk 中注册, 消费者在 zk 中注册。

3 Kafka 消费过程分析

  • 消费模型
  • 消息由生产者发布到 Kafka 集群后,会被消费者消费。消息的消费模型有两种:推送模型(push)和拉取模型(pull)。
  • 基于推送模型(push)的消息系统,由消息代理记录消费者的消费状态。消息代理在将消息推送到消费者后,标记这条消息为已消费,但这种方式无法很好地保证消息被处理。比如,消息代理把消息发送出去后,当消费进程挂掉或者由于网络原因没有收到这条消息时,就有可能造成消息丢失(因为消息代理已经把这条消息标记为已消费了,但实际上这条消息并没有被实际处理)。如果要保证消息被处理,消息代理发送完消息后,要设置状态为“已发送”,只有收到消费者的确认请求后才更新为“已消费”,这就需要消息代理中记录所有的消费状态,这种做法显然是不可取的。
  • Kafka 采用拉取模型, 由消费者自己记录消费状态,每个消费者互相独立地顺序读取每个分区的消息。如下图所示,有两个消费者(不同消费者组)拉取同一个主题的消息,消费者 A 的消费进度是 3,消费者 B 的消费进度是 6。消费者拉取的最大上限通过最高水位(watermark)控制,生产者最新写入的消息如果还没有达到备份数量,对消费者是不可见的。这种由消费者控制偏移量的优点是: 消费者可以按照任意的顺序消费消息。比如,消费者可以重置到旧的偏移量,重新处理之前已经消费过的消息;或者直接跳到最近的位置,从当前的时刻开始消费。
  • 在这里插入图片描述
  • 在一些消息系统中,消息代理会在消息被消费之后立即删除消息。如果有不同类型的消费者订阅同一个主题,消息代理可能需要冗余地存储同一消息;或者等所有消费者都消费完才删除,这就需要消息代理跟踪每个消费者的消费状态,这种设计很大程度上限制了消息系统的整体吞吐量和处理延迟。 Kafka 的做法是生产者发布的所有消息会一致保存在 Kafka 集群中,不管消息有没有被消费。用户可以通过设置保留时间来清理过期的数据,比如,设置保留策略为两天。那么,在消息发布之后,它可以被不同的消费者消费,在两天之后,过期的消息就会自动清理掉。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值