分布式专题|最近一直死磕kafka设计原理,都肝吐了

2020-12-07 11:25:09 浏览数 (1)

kafka架构图

在这里插入图片描述

kafka核心控制器

定义

在kafka集群中,会选举出一个broker作为控制器(controller),负责管理集群中所有的分区和副本的状态;

职责

  1. 监听broker变化,通过监听Zookeeper中的/brokers/ids/ 节点方式来实现
  2. 监听topic变化,通过监听Zookeeper中的/brokers/topics节点方式来实现,实时监听topic变化
  3. 管理topic、partition、broker相关的信息
  4. 更新数据的元数据信息,同步到其他的broker节点

选举过程

broker控制器选举的原理是借助于zookeeper的临时节点实现: kafka集群启动时,每个broker都会尝试争当控制器,都会往zookeeper的controller节点注册自己,但是由于zookeerper的特性,如果节点已经创建过,再创建就会失败,所以只会有一个broker创建成功,那么创建成功的broker就会成为控制器;此外其他broker都会监听这个controller节点

在这里插入图片描述

由于controller是临时节点,当控制器broker挂机之后,就会断开与zookeeper的会话连接,临时节点也会消失,其它节点监听到controller节点消失后,就会重新争取controller节点。

Partition副本选择机制

当controller感知到副本leader所在的broker宕机之后,会在当前副本所在的副本列表中选出第一个副本所在的broker作为副本leader,并且要保证这个broker一定要在副本的ISR(存活的副本broker)集合中,如果第一个不存在,则继续尝试第二个第三个,直到满足;不知道为啥kafka作者为什么不直接在ISR集合里面挑,非得多一步操作

消费者消费的offset如何记录?

每个消费者消费所在分区的offset都会记录在kafka的内部topic中(__consumer_offsets),kafka默认会为这个topic创建50个分区,用来抵抗高并发; 提交到这个topic的时候,key是当前消费者所处的消费组ID topic 分区号,value就是当前offset的值,那么kafka会把这个消息发送到哪个分区呢,是由以下公式决定的: hash(consumer group id) % __consumer_offsets主题的分区数(默认50),consumer每次消费前都会从这里获取offset值;

什么是消费者rebalance?

定义

当某个消费组中的消费者挂掉或退出之后,此时就会自动把分配给它的分区分配给其它消费者(没有被指定消费分区的消费者),不仅如此,当有新的消费者加入后也会触发rebalance操作。

Rebalance过程

  1. 选择组协调器(GroupCoordinato) 因为每个消费组的消费offset提交到的分区是确定的,即通过公式hash(consumer group id) % __consumer_offsets主题的分区数计算而来,所以kafka就直接把这个提交的分区所在的副本leader当作组协调器。
  2. 加入消费组 JOIN GROUP 在选好组协调器之后,接下来就是加入消费组阶段,在此阶段的消费者会向 GroupCoordinator 发送 JoinGroupRequest 请求,并处理响应,然后GroupCoordinator 从一个consumer group中选择第一个加入group的consumer作为leader(消费组协调器),把consumer group情况发送给这个leader,接着这个leader会负责制定分区方案。
  3. SYNC GROUP consumer leader通过给GroupCoordinator发送SyncGroupRequest,接着GroupCoordinator就把分区方案下发给各个consumer,他们会根据指定分区的leader broker进行网络连接以及消息消费。

Rebalance分区分配策略

通过在消费者客户端配置参数partition.assignment.strategy 来设置分配策略,默认为range

  • range 假如现在有10个分区,4个消费者,那么第一步计算平均每个消费者分配的分区数:10/4 = 2,这样每个消费者分到两个分区,还剩余 2 个分区,那么把剩下的两个分区分别分给前面两个消费者,最终分配结果: 第一个消费者:0,1,2 第二个消费者:3,4,5 第三个消费者:6,7 第四个消费者:8,9
  • round-robin(轮询分配) 很容易理解,同上有10个分区,4个消费者: 第一个消费者:0,4,8 第二个消费者:1,5,9 第三个消费者:2,6 第四个消费者:3,7
  • sticky
    • 分区尽可能均匀
    • 分区的分配尽可能与上次分配相同

    假如目前分区分配如下: 第一个消费者:0,4,8 第二个消费者:1,5,9 第三个消费者:2,6 第四个消费者:3,7 现在如果第四个消费者挂机,则重新分配后如下: 第一个消费者:0,4,8,7 第二个消费者:1,5,9 第三个消费者:2,6,3 如果两个规则冲突,优先保证第一个原则

producer发布消息过程

  1. 写入方式 生产者使用push模式将消息发布到broker中,每条消息都被追加到partition中,属于顺序写磁盘
  2. broker根据以下规则将消息发布到指定分区中
  • 如果指定分区,则直接发到指定分区
  • 如果没有指定分区,则根据key进行hash 取模分区数,得到分区
  • key和partition 都没有指定,则使用轮训方式
  1. 发送流程

重要参数acks解释:

  • ack=0 指producer把消息发送出去,不需要等待任何回应,就认为消息发送成功
  • ack=1 指producer把消息发送到leader之后,leader把消息写入磁盘,并回复producer ACK,则producer就认为消息发送成功
  • ack=all/-1 指producer把消息发送到leader之后,leader写入磁盘后,还要等待其他副本的ACK,只有都写成功了,leader才给producer回复ACK,这时producer才认为消息发送成功
  1. producer从zookeeper的state节点中中找到副本leader所在brokerid
  2. producer把消息发送到该leader
  3. leader将消息写入log
  4. follower从leader拉取消息,写入本地log后向leader回复ack
  5. leader收到所有ISR里面的副本恢复ACK之后,增加HW,并向producer发送ACK

HW与LEO到底是个啥?

HW俗称高水位,又称消费能消费到的最大offset,LEO是broker内部能看到的最大offset, 那么这个最大的offset是怎么产生的呢? 正常情况下,LEO的offset和HW的offset是相同的 当有新消息发送到leader之后,leader的LEO就会增加,这个时候LEO的offset就与HW的offset不一样了,接下来副本开始拉取leader消息,对应的副本LEO也会增加,等到最后一个副本同步完成消息之后,LEO和HW的offset就会一致,这样做的目的是什么呢?是为了「数据的一致性」,保证消息同步完成后才能对消费者可见。

❝ 微信搜一搜【乐哉开讲】关注帅气的我,回复【干货领取】,将会有大量面试资料和架构师必看书籍等你挑选,包括java基础、java并发、微服务、中间件等更多资料等你来取哦。 ❞

0 人点赞