分布式消息队列kafka了解及选举方式讲解一(差延迟性队列)

1.kafka简单介绍

1.典型应用:异步处理、系统解耦、流量削峰、日志处理
2.核心原理:kafka体系结构以及读写流程
3.具体操作:high level api 以及 low level api

2.分布式队列的优点

1.高可用性
2.可靠性,数据能够持久化
3.可扩展性,可扩展性->高吞吐量

3.分布式消息典型应用

1.异步处理
2.系统解耦(不要考虑rpc、restfulAPI、socket)
3.流量削峰(请求量大于数据处理能力)
4.日志处理

4.kafka体系结构

  • kafka结构定义

在这里插入图片描述

  • 结构图

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
(以topic主题为区分)

5.kafka基础概念

1)主题:

表示一类的信息,例如注册、日志等

2)存储:

broker: 一个节点,可以看成是kafka的进程,或者是一个节点,有一个主broker话事人,称为controler
controler:主要来控制这里面的信息,比如说这些数据存在哪里,怎么来存储,如果需要选举 (选举的方式由zookeeper从broker中来选举controller)
partition:分区,类似ES或其他中间件的方式,主题会拆分成成多个分区来存储数据
分区中的划分:
leader分区:上述图片中红色为为leader分区
replica分区:用来复制作用的,为上述图中白色的分区
(这些分区是由主broker来选举的产生的)

3)zookeeper在kafka中的作用:

1.用来从broker当中选举出controller
2.存储集群信息,比如partion分区放在对应broker的哪个位置

6.补充各种选举的方式

1)zk etcd的方式来进行选举,zk采用paxos选举
2)redis哨兵的选举方式(哨兵监控哪个redis节点的数据最新,就选举哪个节点作为主节点)
3)去中心化的选举,例如mysql、redis、mongo的做法
4)raft选举,也就是etcd的做法,可以自己实现

7.kafka生产消费模式:

1)点对点

  • 简述:

一个生产者、一个消费者和一个消息队列

  • 概念图

在这里插入图片描述

2)订阅发布模式

  • 简述:

多个生产者、多个消费者

  • 概念图

在这里插入图片描述

8.kafka读写流程及效率

  • 背景知识补充
kafka的API:
high  level  api:包括怎么写数据。消费者怎么消费消息,怎么做rebalance,不需要管整个流程

low   level   api:读写策略都是自定义接口

一些问题总结

1.分区信息存储在哪里?

放在zookeeper

2.生产到哪个分区?

需要提供策略:可以自定义存在哪个分区(low level api),或是用kafka定义行为来写(high level api)

kafka定义写到到哪个分区的行为:
1)随机:已经拿到3个分区:p0\p1\p3的列表,随机去写一个分区
2)hash:每一个消息都是<key,msg>这样的格式,若有3个节点,那就用key对key取余,然后对对应节点存数据(这里的节点指的是上图的p0、p1、p2主分区)
3)轮询(round-robin):producer记录上次存储数据的分区,平均的将消息分布到不同的 partition 上,从而避免某些 partition 数据量过大影响 Broker 和消费端性能

3.怎么确保写流程消息一定能够到达?

采用请求回应的方式 :ack行为
1)ack = 0,不需要返回(不需要考虑安全性),20W吞吐量
2)ack = 1 ,直接返回(主流程处理完直接返回)
3)ack = -1 ,需要等待副本全部同步完成后才会返回,数据丢失可能行最低,最安全,8-10W的吞吐量

4.读流程从哪里开始消费?

4.1 从哪里开始消费?(需要知道具体分区消费到哪个位置)
1)kafka 0.9版本之前,消费的位置存储在zk(zk是高可用、强一致性的key-value存储)
2)kafka  0.9之后采取额外的主题存储消费到哪个位置
4.2 消费者组如何消费?(消费者组与partition对应的问题)

前提:尽量保持消息有序执行

1)背景:分区少,消费者多
做法:一个分区只能对应一个消费者
2)背景:分区多,消费者少
做法:可以两个分区对应一个消费者,也就是一个消费者可以对应多个分区

4.3 怎么确保消息一定被执行消费?(消息可能丢失)

  • 提交策略

1)自动提交:high level api提供
2)手动提交:相对于自动提交,可靠性更高,比如主动提交到mysql或redis,才提示提交完成,保证了数据不丢失

5.消费者的事务问题

流程:消费者从消息队列取数据给db
没用事务的缺点:db更新数据后,若消费者已经宕机,或造成重复消费的问题
解决方案:在db中提交策略,db中存储offset,db存好之后返回ack

7.那么 Partition 应该由那个消费者进行消费,决定因素有哪些呢?

从之前的图中不难得出,两个重要因素分别是:ZK存的消费组中存活的消费者列表Topic 对应的 Partition 列表。通过这两个因素结合 Partition 分配算法,即可得出消费者与 Partition 的对应关系,然后将信息存储到 ZK 中。Kafka 有高级 API 和低级 API,如果不需要操作 OffSet 偏移量的提交,可通过高级 API 直接使用,从而降低使用者的难度。对于一些比较特殊的使用场景,比如想要消费特定 Partition 的信息,Kafka 也提供了低级 API 可进行手动操作。

9.读写流程

1)写流程(数据从生产到partition->双端队列->broker以日志落盘)

  • 流程图(0.9及0.9之前的图,0.9之后版本的kafka消费信息存在额外的主题中):

在这里插入图片描述
在这里插入图片描述
从图中可以看出,左侧按照 partition 维度发送消息,每个 partition 都需要和 Broker 建连,总共发生了四次网络连接。而右侧将分布在同一个 Broker 的 partition 按组聚合后在与 Broker 建连,只需要两次网络连接即可。所以Kafka 选择右侧的方式

  • 流程:
1)首先producer去zookeeper拿到partition主分区链表的信息
2)producer利用三种方式把数据写到主分区,例如ack = 1,写到broker0的p0,等待broker1和broker2的p1和p2同步数据,然后返回ack到producer
(备注:一条消息首先需要确定要被存储到那个 partition 对应的双端队列上;其次,存储消息的双端队列是以批的维度存储的,即 N 条消息组成一批,一批消息最多存储 N 条,超过后则新建一个组来存储新消息;其次,新来的消息总是从左侧写入,即越靠左侧的消息产生的时间越晚;最后,只有当一批消息凑够 N 条后才会发送给 Broker,否则不会发送到 Broker 上)
  • 写入到kafka怎么存?(以固定的大小的日志文件LogSegment去存)

在这里插入图片描述

  • 数据到kafka的落盘流程

备注:在第 3 篇文章讲过,生产者客户端对于每个 Partition 一次会发送一批消息到服务端,服务端收到一批消息后写入相应的 Partition 上。上图流程主要分为如下几步:)

1)客户端消息收集器收集属于同一个分区的消息,并对每条消息设置一个偏移量,且每一批消息总是从 0 开始单调递增。比如第一次发送 3 条消息,则对三条消息依次编号 [0,1,2],第二次发送 4 条消息,则消息依次编号为 [0,1,2,3]。注意此处设置的消息偏移量是相对偏移量。
2)客户端将消息发送给服务端,服务端拿到下一条消息的绝对偏移量,将传到服务端的这批消息的相对偏移量修改成绝对偏移量。
3)将修改后的消息以追加的方式追加到当前活跃的 LogSegment 后面,然后更新绝对偏移量。
4)将消息集写入到文件通道。
5)文件通道将消息集 flush 到磁盘,完成消息的写入操作。

  • 消息的具体构成

在这里插入图片描述
1)OffSet:偏移量,消息在客户端发送前将相对偏移量存储到该位置,当消息存储到 LogSegment 前,先将其修改为绝对偏移量在写入磁盘。
2)Size:本条 Message 的内容大小
3)Message:消息的具体内容,其具体又由 7 部分组成,crc 用于校验消息,Attribute 代表了属性,key-length 和 value-length 分别代表 key 和 value 的长度,key 和 value 分别代表了其对应的内容。

2)读流程

  • 流程图(0.9及0.9之前的图,0.9之后版本的kafka消费信息存在额外的主题中)

在这里插入图片描述

  • 对应关系改变:
    注意:kafka对应关系改变,先暂停服务,然后rebalance,也就是说rebalance是有代价的

high level api:
1.订阅发布模式下:
1)rebalance模式,修复脆弱的对应关系(比如说消费者或生产者)(high level api)(也叫repatition)
low level api:
1)range 平均算法分配对应关系,比如严格一个消费者对应两个分区
2)轮询 分区分别开始依次给消费者询问
3)粘性的方式
没有发生rebalance的时候使用轮询的方式,发生rebalance了,尽量保证原来的对应关系不变,后面采用轮询的方式`

  • repartition/rebalance

1)消费组内某消费者宕机,触发 Repartition 操作,如下图所示。
在这里插入图片描述
2)消费组内新增消费者,触发 Repartition 操作,如下图所示。一般这种情况是为了提高消费端的消费能力,从而加快消费进度。
在这里插入图片描述
3)Topic 下的 Partition 增多,触发 Repartition 操作,如下图所示。一般这种调整 Partition 个数的情况也是为了提高消费端消费速度的,因为当消费者个数大于等于 Partition 个数时,在增加消费者个数是没有用的(原因是:在一个消费组内,消费者:Partition = 1:N,当 N 小于 1 时,相当于消费者过剩了),所以一方面增加 Partition 个数同时增加消费者个数可以提高消费端的消费速度。
在这里插入图片描述

  • 消费者与消费组的“父子关系”。

在这里插入图片描述
1)在同一个消费者组内,一个 Partition 只能被一个消费者消费。
2)在同一个消费者组内,所有消费者组合起来必定可以消费一个 Topic 下的所有 Partition。
3)在同一个消费组内,一个消费者可以消费多个 Partition 的信息。
4)在不同消费者组内,同一个分区可以被多个消费者消费。
5)每个消费者组一定会完整消费一个 Topic 下的所有 Partition。

  • 消费信息流程

1)客户端确定拉取的位置,即 StartOffSet 的值,找到主副本对应的 LogSegment。
2)LogSegment 由索引文件和数据文件构成,由于索引文件是从小到大排列的,首先从索引文件确定一个小于等于 StartOffSet 最近的索引位置。
3)根据索引位置找到对应的数据文件位置,由于数据文件也是从小到大排列的,从找到的数据文件位置顺序向后遍历,直到找到和 StartOffSet 相等的位置,即为消费或拉取消息的位置。
4)从 StartOffSet 开始向后拉取 MaxLength 大小的数据,返回给消费端或者从副本进行消费或备份操作。
在这里插入图片描述

  • 总结

本文从逻辑存储和物理存储的角度,分析了消息的写入与消费流程。其中逻辑存储是以 Partition 来管理一批一批的消息,Partition 映射 Log 对象,Log 对象管理了多个 LogSegment,多个 Partition 构成了一个完整的 Topic。消息的实际物理存储是由一个一个的 LogSegment 构成,每个 LogSegment 又由索引文件和数据文件构成。下篇文章我们来分析一些实际生产环境中的常用操作及数据接入方案,敬请期待。

Partition(映射)->Log对象(管理)->多个LogSegment,这样多个Partition又构成了一个完整的Topic

10.延时队列实现(未完成施工)

1)kafka实现延时队列

  • 实现方法:
1)创建额外的主题
2)还要有额外的定时进程检测这个主题,检测到了就把这个主题的数据放到消息队列特定的主题中,消费者就去具体的消息队列中消费

2)rabbitMQ

3)自己实现延时队列

可以用redis的zset有序集合,可以实现数据的有序性

11.KafkaController 选举策略

  • 背景:zk目录节点

稍微熟悉 Zookeeper 的小伙伴应该都比较清楚,Zookeeper 是通过监控目录(zNode)的变化,从而做出一些相应的动作。
Zookeeper 的目录分为四种,
(1)第一种是永久的,被称作为 Persistent;
(2)第二种是顺序且永久的,被称作为 Persistent_Sequential;
(3)第三种是临时的,被称为 Ephemeral;
(4)第四种是顺序且临时的,被称作为 Ephemeral_Sequential。

  • 流程

KafkaController 正是利用了临时的这一特性来完成选主的,在 Broker 启动时,每个 Broker 的 KafkaController 都会向 ZK 的 /controller 目录写入 BrokerId,谁先写入谁就是 Leader,剩余的 KafkaController 是 Follower,当 Leader 在周期内没有向 ZK 发送报告的话,则认为 Leader 挂了,此时 ZK 删除临时的 /controller 目录,Kafka 集群中的其他 KafkaController 开始新的一轮争主操作,流程和上面一样。下面是选 Leader 的流程图。
在这里插入图片描述

12.Partition 分布算法

图中红色部分为p3
图中红色部分为P3
图解:假设集群有 3 个 Broker,Partition 因子为 2。

  • 流程

1)随机选取 Broker 集群中的一个 Broker 节点,然后以轮询的方式将主 Partition 均匀的分布到不同的 Broker 上。
2)主 Partition 分布完成后,将从 Partition 按照 AR 组内顺序以轮询的方式将 Partition 均匀的分布到不同的 Broker 上。

13.Partition 的状态转移

  • 介绍

用户针对特定的 Topic 创建了相应的 Partition ,但是这些 Partition 不一定时刻都能够正常工作,所有 Partition 在同一时刻会对应 4 个状态中的某一个;其整个生命周期会经历如下状态的转移,分别是
1)NonExistentPartition:代表没有创建 Partition 时的状态,也有可能是离线的 Partition 被删除后的状态。
2)NewPartition:当 Partition 被创建时,此时只有 AR(Assigned Replica),还没有 ISR(In-Synic Replica),此时还不能接受数据的写入和读取。
3)OnlinePartition:由 NewPartition 状态转移为 OnlinePartition 状态,此时 Partition 的 Leader 已经被选举出来了,并且也有对应的 ISR 列表等。此时已经可以对外提供读写请求了。
4)OfflinePartition:当 Partition 对应的 Broker 宕机或者网络异常等问题,由 OnlinePartition 转移到 OfflinePartition,此时的 Partition 已经不能在对外提供读写服务。当 Partition 被彻底删除后状态就转移成 NonExistentPartition,当网络恢复或者 Broker 恢复后,其状态又可以转移到 OnlinePartition,从而继续对外提供读写服务。
其对应的状态转移情况如下图所示。

在这里插入图片描述

14.Kafka 集群的负载均衡处理流程解析(rebalance之粘性的做法)

如果所示:
在这里插入图片描述

  • 流程解释

从上图可以看出,集群运行一段时间后,Broker1 挂掉了,在其上运行的 Partition0 对应的 Leader Replica 转移到了 Broker2 上。假设一段时间后 Broker3 也挂了,则 Broker3 上的 Partition3 对应的 Leader Replica 也转移到了 Broker2 上,集群中只有 Broker2 上的 Partition 在对外提供读写服务,从而造成 Broker2 上的服务压力比较大,之后 Broker1 和 Broker3 恢复后,其上只有 Preferred Replica 做备份操作。

  • 原理:

重平衡操作是由 partition-rebalance-thread 后台线程操作的,由于其优先级很低,所以只会在集群空闲的时候才会执行。集群的不平衡的评判标准是由leader.imbalance.per.broker.percentage配置决定的,当集群的不平衡度达到 10%(默认)时,会触发后台线程启动重平衡操作,其具体执行步骤如下:

  • 流程
1)对 Partition 的 AR 列表根据 Preferred Replica 进行分组操作。
2)遍历 Broker,对其上的 Partition 进行处理。
3)统计 Broker 上的 Leader Replica 和 Preferred Replica 不相等的 Partition 个数。
4)统计 Broker 上的 Partition 个数。
5)Partition 个数 / 不相等的 Partition 个数,如果大于 10%,则触发重平衡操作;反之,则不做任何处理。

猜你喜欢

转载自blog.csdn.net/weixin_43679037/article/details/120768175