Kafka—消息队列(理论部分)

一、Kafka概述

1.1、简介

kafka是一个分布式的基于发布/订阅模式消息队列

主要应用场景:大数据实时处理领域

1.2、什么是消息队列?

消息队列 = 消息 + 队列

消息 : 说白了就是数据(请求数据、业务数据等等)

队列 : 就是队列(数据结构中线性表或链表实现的先入先出的队列)

消息队列就是存放数据的队列,一种容器而已,消息队列的概念很简单,但结合实际的应用场景便很复杂

image-20201113200151456

如图便是消息队列最简单的消息队列模型

  • 向消息队列中存放数据的叫做生产者

  • 从消息队列中获取数据的叫做消费者

1.3、消息队列模式

消息队列有两种

1.3.1、点对点模式(消费者主动拉取数据)

在这里插入图片描述

生产者将数据放在消息队列中,消费者消费数据后,消息队列会将数据删除,队列支持可以存在多个消费者,但一个消息能由其中一个消费者消费,即消息和消费者之间是一对一的

1.3.2、发布/订阅模式

在这里插入图片描述

如图发布\订阅模式有一个主题的概念,生产者定义主题,将消息存放在相应的主题中,消费者订阅主题,从该主题中获取数据进行消费,如图该模式下,允许多个消费者订阅同一主题,主题中的每个消息可由多个消费者进行消费,即:消息与消费者之间是一对多的

1.3.3、消费者获取数据的两种方式

  • pull:消费者主动拉取;数据消费者决定自己何时请求并接收数据,数据持有者只能被动地响应请求
  • push:生产者主动推送;数据生产者决定何时向消费者推送数据。数据消费者不知道何时会收到数据更新

二、Kafka分布式基础架构

在这里插入图片描述

如图是Kafka的基础架构:

  • Kafka是一个基于分布式的发布订阅模式的消息队列,所以在真实场景中,Kafka是分布式部署在集群上
  • Topic:主题,就是一个消息队列,一个Kafka集群上运行者多个主题,也就是多个消息队列
  • Partition:分区,为了增强可扩展性,提高吞吐量,引入了分区,一个主题可以被分成多个分区,每个分区运行在集群的一个节点上
  • leader:每个节点可能同时存在多个主题的分区,为了增强集群的高可用,为每个主题的分区都提供了副本,一个分区可以有多个副本,但只有一个能被选为leader,被选为leader的分区对外提供服务,即生产者只将数据存放在leader中,消费者只从leader中获取数据
  • follew:leader的副本,一般情况下只将leader中的数据进行同步,当leader挂掉了,有重新选择一个副本作为leader,对外服务,目的提高系统的可用性
  • CG:消费者组,逻辑意义上的消费者,一个消费者组中有多个消费者,这些消费者订阅了同一个主题,也可能订阅不同的主题,每个消费者消费不同分区的数据,一个分区只能被一个消费者消费,消费者组之间互不影响

三、 Kafka架构深入

3.1、offset及工作流程

在这里插入图片描述

如图所示,在kafka中,消息使用topic(主题)来分类的,而Topic是抽象的,分区是物理上的,kafka为每个分区都创建了一个log文件,文件存储了分区中的数据,生产者生产的数据会不断被追加到log文件的末尾,每条数据都有自己的offset,消费者都会实时记录自己消费到那个offset,即消费到了哪个消息了,如果出错了,就会从上次的offset记录处继续消费

3.2、文件存储机制

由于数据是源源不断到来的,为了避免log文件被越存越大,提高数据查找的效率,kafka引入了分片索引机制,将文件分割成许多小文件,为了快速定位数据的位置,又提供了数据的索引,即元数据,降低了每次检索数据的开销和IO

在这里插入图片描述

如图Segment就是一个分片,.log文件中存放着该分片中的数据,.index中存放着数据的索引

在物理逻辑上:

  • 一个分区对应着一个文件夹 topic名+分区编号
  • 每个分区的文件夹下存放着分区对应的分片文件,随着数据越来越多,分区也会越来越多

3.3 生产者使用分区的策略

解决生产者使用消息队列的问题:

  • 怎样选择分区?
  • 保证数据的可靠性?
  • 故障处理?

在这里插入图片描述

3.3.1、分区原因

  • 提高扩展性:分片分布式部署,有利于集群扩展
  • 提高吞吐量:一个主题,分为多个分区,分区分布在服务器集群中,可以处理大量数据
  • 提高并发:以分区为单位读写

3.3.2、生产者使用分区原则

  1. 指明分区的情况下,直接将指明的值作为消息要推送的分区
  2. 没有指明分区,但设置了key,将key的hash值,与分区总数取余等到的余数为消息要推送的分区
  3. 没有 partition 值没有 key 值的情况下,取一个随机数,与分区可用的总数取余等到的余数为消息要推送的分区

3.3.3 数据可靠性保证

image-20201113235448095

如图,生产者向指定的主题发送消息,主题的分区接收到消息后需要给生产者发送确认消息,生产者收到确认消息后,将发送下一波消息,否则再次发送

为了保证可用性,分区收到消息,副本要进行同步,有可能在未同步完成时,leader节点挂掉了,如果又向生产者发送了确认信息,新的leader分区,又没有新的数据,数据便发生了丢失

因此存在了两个问题:==(1)==何时向生产者发送确认信息,才能保证数据的完整可靠?对此有两种策略:

  • 全部副本同步完成了再发送
  • 半数以上同步完成了再发送
1)副本数据同步策略
方案优点缺点
半数以上完成同步,就发送ack延迟低选举新的leader时,容忍n台节点的故障,需要2n+1个副本
全部完成同步,才发送ack选举新的leader时,容忍n台节点的故障,需要n+1个副本延迟高

Kafka选择了第二种方案,原因如下:

​ 1.同样为了容忍n台节点的故障,第一种方案需要2n+1个副本,而第二种方案只需要n+1个副本,而Kafka的每个分区都有大量的数据,第一种方案会造成大量数据的冗余。

​ 2.虽然第二种方案的网络延迟会比较高,但网络延迟对Kafka的影响较小。

==问题二:==采用第二种方案之后,设想以下情景:leader收到数据,所有follower都开始同步数据,但有一个follower,因为某种故障,迟迟不能与leader进行同步,那leader就要一直等下去,直到它完成同步,才能发送ack。这个问题怎么解决呢?

Leader维护了一个动态的和leader保持同步的follower集合。当ISR中的follower完成数据的同步之后,leader就会给follower发送ack。如果follower长时间未向leader同步数据,则该follower将被踢出ISR,该时间阈值由replica.lag.time.max.ms参数设定。

Leader发生故障之后,就会从ISR中选举新的leader。

2) ack应答机制

并不是所有的数据都要进行保证,可靠对于有些数据必须完整可靠,而有些数据丢失一部分也无关紧要;

ack应答级别分为三种情况,对应ack参数的三种设置:

  • 0 生产者不等待,即leader一收到数据就返回ack(未写入磁盘),在leader发送故障时,可能引起数据丢失
  • 1 生产者等待,leader落盘成功后,返回ACK,在副本未同步成功之前,leader故障,数据引起丢失
  • -1 生产者等待,所有leader和副本落盘成功后,发送ack,但在成功后,ack发送之前,leader故障,可能造成数据重复
3)故障处理细节

在这里插入图片描述

如图:kafka为了更好的处理故障,在每个分区包括副本的log文件里引入了两个参数

  • LEO(Log end offset):每个副本中最后一个offset(leader中也有)

  • HW(High WaterMark):所有副本中最小的LEO(之前的数据才对消费者可见)

(1)follower故障

follower故障会被踢出ISR,等其恢复后,会读取上次的HW的值,并将HW之后的数据截掉,重新从leader同步数据,等其同步追上leader后(LEO大于该分区的最新HW),重新加入ISR

(2)leader故障

leader故障后,会从ISR中选出新的leader,为了保证数据的一致性,所有的副本会将高于HW的数据都截掉,重新从leader中同步

这只保证副本间数据的一致性,不能保证数据不丢失,与不重复

3.3.4 Exactly Once语义

保证每条数据有且仅被发送一次配合ack=-1,保证数据的不丢失,不重复

使用时,只需将enable.idempotence属性设置为true,kafka自动将acks属性设为-1。

3.4、消费者消费策略

在这里插入图片描述

看看这部分的问题:

  • 消费方式?
  • 分区分配策略?
  • offset的维护?

3.4.1、消费者的消费方式

消费者消费方式前面提到过两种方式:

  • 消费者拉取pull
  • 有broker推送 push

由于在推送时,由于数据的发送速度是由broker决定的,但一般消费者接收的速率比较低,很容易造成consumer来不及处理消息,典型的表现就是拒绝服务以及网络拥塞,所以消费者才用了拉取:即消费者主动去拉取数据

pull模式不足之处是,如果kafka没有数据,消费者可能会陷入循环中,一直返回空数据。针对这一点,Kafka的消费者在消费数据时会传入一个时长参数timeout,如果当前没有数据可供消费,consumer会等待一段时间之后再返回,这段时长即为timeout。

3.4.2 分区分配策略

消费者组有多个消费者,一个主题有多个分区,一个分区只能由一个消费者消费,所以涉及到了分区的分配策略

Kafka有两种分配策略:

  • roundrobin --轮询

    RoundRobin 轮询分区策略,是把所有的 partition 和所有的 consumer 都列出来,然后按照 hascode 进行排序,最后通过轮询算法来分配 partition 给到各个消费者。

    两种情况:

    • 组内消费者订阅的主题是相同的

      那么 RoundRobin 策略的分区分配会是均匀的。

    • 组内消费者订阅的主题是不同的

      有可能会导致分区分配的不均匀。如果某个消费者没有订阅消费组内的某个 topic,那么在分配分区的时候,此消费者将不会分配到这个 topic 的任何分区。

  • range

    Range 范围分区策略是对每个 topic 而言的。首先对同一个 topic 里面的分区按照序号进行排序,并对消费者按照字母顺序进行排序。假如现在有 10 个分区,3 个消费者,排序后的分区将会是0,1,2,3,4,5,6,7,8,9;消费者排序完之后将会是C1-0,C2-0,C3-0。通过 partitions数/consumer数 来决定每个消费者应该消费几个分区。如果除不尽,那么前面几个消费者将会多消费 1 个分区。

    弊端:

    如上,只是针对 1 个 topic 而言,C1-0消费者多消费1个分区影响不是很大。如果有 N 多个 topic,那么针对每个 topic,消费者 C1-0 都将多消费 1 个分区,topic越多,C1-0 消费的分区会比其他消费者明显多消费 N 个分区

3.4.3、触发分区分配策略

  • 消费者变化时
  • 分区变化时

3.4.4 offset的维护

​ Kafka 0.9版本之前,consumer默认将offset保存在Zookeeper中,从0.9版本开始,consumer默认将offset保存在Kafka一个内置的topic中,该topic为**__consumer_offsets**。

3.5 Kafka 高效读写数据

3.5.1、顺序写磁盘

顺序写之所以快,是因为其省去了大量磁头寻址的时间。

Kafka的producer生产数据,要写入到log文件中,写的过程是一直追加到文件末端,为顺序写。官网有数据表明,同样的磁盘,顺序写能到到600M/s,而随机写只有100k/s。

3.5.2、零复制技术

一般复制:

在这里插入图片描述

该复制方式需要经过核心态与用户态的复制传递,比较耗时,开销也大

零复制:

在这里插入图片描述

优化了中间过程,

提高了效率

四、 Zookeeper在Kafka中的作用

Kafka是依赖于zookeeper集群

Kafka集群中有一个broker会被选举为Controller,负责管理集群broker的上下线,所有topic的分区副本分配和leader选举等工作。并且都是依赖于Zookeeper的

在这里插入图片描述

如图就是Kafka集群中Controller的选举过程(也就是leader)可能是为了与分区的leader区分

上图中zookeeper中维护了两种数据:

  • 集群中所有机器broker的编号
  • 每个主题中分区与副本信息

kafkaController监听zookeeper中broker信息,有节点故障、上下线时,通过使用zookeeper中节点注册信息,维护主题,分区,副本,ISR等信息,协调维持集群的稳定

p9juzu-1605326382881)]

如图就是Kafka集群中Controller的选举过程(也就是leader)可能是为了与分区的leader区分

上图中zookeeper中维护了两种数据:

  • 集群中所有机器broker的编号
  • 每个主题中分区与副本信息

kafkaController监听zookeeper中broker信息,有节点故障、上下线时,通过使用zookeeper中节点注册信息,维护主题,分区,副本,ISR等信息,协调维持集群的稳定

Logo

Kafka开源项目指南提供详尽教程,助开发者掌握其架构、配置和使用,实现高效数据流管理和实时处理。它高性能、可扩展,适合日志收集和实时数据处理,通过持久化保障数据安全,是企业大数据生态系统的核心。

更多推荐