Kafka 原理详细分析

130 阅读6分钟

www.processon.com/diagraming/…

0

Kafka 原理详细分析


  1. Controller 核心控制器

kafka集群中,存在一个或者多个Broker,需要对这些个 Broker进行监控管理,因此,需要在这些个 Broker中选举出一个Broker成为Controller,它负责整个集群中选举的工作。包括以下两种:

  • 当某个分区中的某个Leader出现了故障,负责选举产生新的Leader,从 ISR列表 中选举一个Broker成为新的Leader(默认选择第一个Broker作为Leader)
  • 当 ISR列表 出现变化(新增或者删除),它将负责通知其他Broker更新元数据
  • 当新增分区时,它将分则 通知其他broker,为当前分区选举broker成为其leader

1.1. Controller 选举机制

在kafka集群中,使用脚本启动broker,都会竞争去zookeeper中创建一个 /controller的临时节点,zookeeper保证只有一个broker能成功创建controller临时节点,这个broker就成为了controller(总控制器)。

当controller这个broker宕机了,其他broker监听到controller这个broker宕机了,则会竞争去zookeeper中创建/controller节点。这是也会选举产生一个新的controller。

具备controller功能的broker节点,仅仅是在去承担一份原broker该有职责之外的工作:

  1. 监控broker的变化:监控着/broker/ids中数组的变化(数组里面维护的是各个broker节点ID)
  2. 监听Topic的变化:新增一个Topic,需要通知broker来对其进行操作(包括Partion的Leader选举)
  3. Partition的变化:从zk中读取所有Topic、partition以及broker相关信息并且进行管理,监控着所有topic下partition的变化
  4. 更新元数据:broker发生变化,则通知所有的broker更新元数据

1.2.Partitions 选举Leader机制

controller 监控到是Leader的Partition的broker挂掉了,则需要从 ISR列表中选举出一个broker成为Leader,分为两种情况,通过设置参数:unclean.leader.election.enable (默认值false)

  • unclean.leader.election.enable = false,从ISR列表中选举第一个broker成为partitions的leader
  • unclean.leader.election.enable = true,如果ISR列中没有broker,则从replicas 中选择一个broker成为leader,这种方式提高了可用性,但是带来的问题就是数据可能会少很多

follower节点进入到ISR列表中的条件需要满足以下:

  1. 保证与leader能进行网络传输

  2. follower能复制leader上所有的写操作

  3. Producer 发送Message机制

producer 采用 push 模式将消息发布到broker,每条消息都被 append 到 partition 中,这个 append 的过程属于 顺序写入磁盘(顺序写入磁盘比随机写入内存速度要快,保证了kafka的吞吐量)

producer 发送消息到 broker ,会根据分区算法选择存储到哪一个 partition 中,其路由机制:

  • 如果指定了 发送到 partition,则无需使用路由算法,直接存储到指定的patition(leader)
  • 如果没有指定 partition,则需要根据传入的 key 经过hash计算后,除以当前 TOPIC 总分区数,取模得到存储到那个 topic 上的哪个 partition 上,(公式:hash(key)%sum(partition))

0

  • 如果 partition 和 key 都没有指定,则使用 负载均衡 选出一个 partition。

2.1. producer 发送流程

0

流程步骤分为以下几步:

  1. Producer send 一条消息,首先需要通过分区算法,确定在发送消息时,是否指定了 partition,如果指定了,则直接发送给 Leader,执行 writer Log 操作,否则,需要需要进一步判断,是否指定key的value值,如果指定了,则经过hash计算得到对应 partition ,然后执行 writer 操作。如果都没有指定,则通过轮询算法,直接选择一个 partition ,进行writer Log 操作。(拿到 partition)

  2. 发送 Message 给 partition (Leader)

  3. patition(Leader) writer Message 到 Local Log

  4. follower 从 Leader 中 拉取Message

  5. follower writer Message 到 follower Log

  6. follower 给 Leader 应答

  7. Leader 收到应答后,则执行 commit offset 操作,并且发送 ack 给 producer。

  8. Consumer消费Message的offset机制

consumer 在提交后,会将offset提交给当前分区的kafka内部topic,用于记录最新offset的值:__consumer_offset ,提交过去的 数据 key = consumerGroup+topic+分区号,value = offset的值,kafka会定期清理topic中的数据。

原因是因为kafka默认给的分区数是0到49(50个分区)。

3.1. Consumer的Rebalance(重平衡)机制

如果指定分区,则不会出现 rebalance ,在没有指定 partition 的情况下,会出现 rebalance,以下几种情况会打破平衡:

  1. consumer Group 中 cusomer 的变化(新增或者减少)
  2. topic 的变化 (新增或者减少)

注意:rebalance 过程中,consumer 处于阻塞状态,无法从 kafka 服务器中提取消息,进行消费。因此发生 rebalance 会影响 kafka 的 tps 。

3.1.1. Rebalance 过程

  1. consumer 启动的时候,发出请求,与选择 coordinator(组协调器)建立连接,这时,coordinator 就具有了监控consumer的能力,可以监控consumer的心跳,判断是否宕机,是否发起 rebalance。 coordinator 选择通过 hash(group_id)% __consumer_offset(默认50个分区)
  2. 当有 consumer 加入到 consumerGroup 组时,首先需要向 coordinator 发出 join Group Requst 请求,coordinator 处理完成后,并且响应结果,如果此时 consumer 是第一个加入到 consumerGroup 中的,则将被推举为 consumer Leader,用于给 consumerGroup 中其他的 consumer 制定分区策略。
  3. 分区策略制定完成后,Leader 需要将结果发送 coordinator ,接着 coordinator 会将分区的方案下发给 consumer。

rebalance 策略:

分为3种:range(默认)、round-robin、sticky

range:用topic中 sum(partition)与 consumer-group 中 sum(consumer)做计算,得到计算结果,重新将分区分配给那个consumer进行消费,计算公式如下:

m = sum(partition) / sum(consumer) , n = sum(partition) % sum(consumer)

如:patition = 10,consumer = 3,经过计算:m = 3,n = 1 ,分区分配方案:consumer_1 = m+1 =【P0,P1,P2,P3】,consumer_2 = sum(consumer) - n = 【P4,P5,P6】,consumer_3 = sum(consumer) - n = 【P7,P8,P9】

round-robin:轮询分配策略,如consumer_1 = m+1 =【P0,P3,P6,P9】,consumer_2 = sum(consumer) - n = 【P1,P4,P7】,consumer_3 = sum(consumer) - n = 【P2,P5,P8】

sticky:均匀分配策略,如果发生 rebalance 机制,consumer 的个数将会发生改变,但是必须满足以下2个规则:

  1. 分区分配必须均匀的分配给consumer
  2. 分区分配方式尽量保持与未发生rebalance之前的分配规则一致

备注:如果以上两个规则产生分歧,优先保证规则1。如 group_consmer 中 3个 consumer,假设最后一个 cosumer 挂掉了,则需要将这个consumer消费的分区,分给consumer1,consumer2,同时保证这两个消费者得到分片后,总数上保持均匀。

  1. kafkaLog日志分段存储

kafka一个分区的消息存储在一个文件夹下,以topic名称 + 分区号命名,消息在分区内是分段存储的,每一段的消息都存储在不一样的log文件里,这个特性方便删除,且kafka规定一个log日志最大存储1个G的的内容,这样方便快速加载。

0

由图可以知道,kafka为每个分区都维护了一个 segment 列表,记录了分区内每一个日志文件的offset开始到达到1G时的offset,指向 segment 日志文件。

这样方便于删除,当删除时,只需要将 segment 列表中维护这个日志移除掉,则将没有引用只想segment file。

添加日志时,也只需要在 segment 列表最后,添加一个引用,然后指向分区下的 log 文件即可。