kafka学习之基本原理

kafka是一个分布式的,基于发布/订阅模式的消息队列(Message Queue), 主要应用于大数据实时处理领域

架构图:

 

 

kafka工作流程:

 

 

kafka流处理平台具有三个关键能力:

  1. 发布和订阅消息(流),在这方面,它类似于一个消息队列。
  2. 以容错(故障转移)的方式存储消息(流)。
  3. 在消息流发生时处理它们。

应用:

  1. 构建实时的流数据管道,可靠地获取系统和应用程序之间的数据。
  2. 构建实时流的应用程序,对数据流进行转换或反应。

 结构:

  1. kafka作为一个集群运行在一个或多个服务器上。
  2. kafka集群存储的消息是以topic为类别记录的。
  3. 每个消息(也叫记录record,我习惯叫消息)是由一个key,一个value和时间戳构成。

kafka有四个核心API:

  • 程序使用 Producer API 发布消息到1个或多个topic(主题)中。
  • 程序使用 Consumer API 来订阅一个或多个topic,并处理产生的消息。
  • 程序使用 Streams API 充当一个流处理器,从1个或多个topic消费输入流,并生产一个输出流到1个或多个输出topic,有效地将输入流转换到输出流。
  • Connector API 可构建或运行可重用的生产者或消费者,将topic连接到现有的应用程序或数据系统。
Client和Server之间的通讯,是通过一条简单、高性能并且和开发语言无关的TCP协议。并且该协议保持与老版本的兼容。
kafka基本概念:

topic:Kafka将消息分门别类,每一类的消息称之为一个主题(Topic)。

Producer:发布消息的对象称之为主题生产者(Kafka topic producer)

Consumer:订阅消息并处理发布的消息的对象称之为主题消费者(consumers)

Broker:已发布的消息保存在一组服务器中,称之为Kafka集群。集群中的每一个服务器都是一个代理(Broker)。 消费者可以订阅一个或多个主题(topic),并从Broker拉数据,从而消费这些已发布的消息。

一个是".index"结尾的文件,文件位于一个文件夹下,该文件夹的命名规则为:topic 名称+分区序号。存储message的offset和offset之前message的字节数大小,帮助快速定位数据,如下图示例

 

 

 

主题和日志 (Topic和Log)

Topic是发布的消息的类别名,一个topic可以有零个,一个或多个消费者订阅该主题的消息。

对于每个topic,Kafka集群都会维护一个分区log,就像下图中所示:

 

 每一个分区都是一个顺序的、不可变的消息队列, 并且可以持续的添加。分区中的消息都被分了一个序列号,称之为偏移量(offset),在每个分区中此偏移量都是唯一的。

Kafka集群保持所有的消息,直到它们过期(无论消息是否被消费)。实际上消费者所持有的仅有的元数据就是这个offset(偏移量),也就是说offset由消费者来控制:正常情况当消费者消费消息的时候,偏移量也线性的的增加。但是实际偏移量由消费者控制,消费者可以将偏移量重置为更早的位置,重新读取消息。可以看到这种设计对消费者来说操作自如,一个消费者的操作不会影响其它消费者对此log的处理。

 

 Kafka中采用分区的设计有几个目的。一是可以处理更多的消息,不受单台服务器的限制。Topic拥有多个分区意味着它可以不受限的处理更多的数据。第二,分区可以作为并行处理的单元。

 

分布式(Distribution)

Log的分区被分布到集群中的多个服务器上。每个服务器处理它分到的分区。 根据配置每个分区还可以复制到其它服务器作为备份容错。 每个分区有一个leader,零或多个follower。Leader处理此分区的所有的读写请求,而follower被动的复制数据。如果leader宕机,其它的一个follower会被推举为新的leader。 一台服务器可能同时是一个分区的leader,另一个分区的follower。 这样可以平衡负载,避免所有的请求都只让一台或者某几台服务器处理。

Geo-Replication(异地数据同步技术)

Kafka MirrorMaker为群集提供geo-replication支持。借助MirrorMaker,消息可以跨多个数据中心或云区域进行复制。 您可以在active/passive场景中用于备份和恢复; 或者在active/passive方案中将数据置于更接近用户的位置,或数据本地化。

生产者(Producers)

生产者往某个Topic上发布消息。生产者也负责选择发布到Topic上的哪一个分区。最简单的方式从分区列表中轮流选择。也可以根据某种算法依照权重选择分区。开发者负责如何选择分区的算法。

生产者数据可靠性的保证:

为保证 producer 发送的数据能够可靠的发送到指定的topic,topic的每个partition收到发送的数据后,都需要向 producer 发送ack(acknowledgement确认收到 ),如果producer收到ack,就会进行下一轮的发送,否则重新发送数据

 

 1)副本数据同步策略

   Kafka 选择了第二种方案(全部同步完成),原因如下:

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

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

2)、ISR同步副本策略

     ISR同步副本集合的目的在于防止leader挂掉后,选取新的leader,尽量不丢失数据(在新版本的kfaka中,采取将与leader同步时间较低的follower加入到ISR集合,作为leader备选。在老版本中,还有将同步的数据条数多的follower加入ISR的策略,在新版本.0.9之后中被剔除了)

由于 kafka 选择了全部同步完成后 发送ack的副本策略,就会存在这样一个问题。

     设想以下情景:leader 收到数据,所有 follower 都开始同步数据,但有一个 follower,因为某种故障,迟迟不能与 leader 进行同步,那 leader 就要一直等下去,而不发送 ack的问题。

解决上述弊端的办法:

我们在查看topic的描述信息时,可以看到 Isr信息

3)ack参数配置

      对于某些不太重要的数据,对数据的可靠性要求不是很高,能够容忍数据的少量丢失,所以没必要等 ISR 中的 follower 全部接收成功。所以 Kafka 为用户提供了三种可靠性级别,用户根据对可靠性和延迟的要求进行权衡,选择以下的配置。

acks参数配置:

     acks 为 0 :producer 不等待 broker 的 ack,这一操作提供了一个最低的延迟,broker 一接收到数据还没有写入磁盘就已经返回,当 broker 故障时有可能丢失数据;

     acks 为 1 :producer 等待 broker 的 ack,partition 的 leader 落盘成功后返回 ack(只等待leader写完,不等待follower),如果在 follower 同步成功之前 leader 故障,那么将会丢失数据;

     acks 为 -1 或 all :producer 等待 broker 的 ack,partition 的 leader 和 follower(指的是ISR中的follower)全部落盘成功后才返回 ack。但是如果在 follower 同步完成后,broker 发送 ack 之前,leader 发生故障,那么会造成数据重复。

acks = -1 造成数据重复的案例(如图所示):

 

 

leader收到数据并且follower完成同步,但是在leader还没发ack是发生故障,会选取一台follower代替leader。而其实producer没有收到ack,会再次发送之前的数据过来,造成了数据重复。

4)故障处理细节(保证生产和接收的数据一致性)

LEO(Log End Offset):指的是每个副本最大的 offset;

HW(High Watermark):指的是消费者能见到的最大的 offset,ISR 队列中最小的 LEO。(已经完成数据同步的最大offset)

(1)follower 故障:

follower 发生故障后会被临时踢出 ISR,待该 follower 恢复后,follower 会读取本地磁盘记录的上次的 HW,并将 log 文件高于 HW 的部分截取掉,从 HW 开始向 leader 进行同步。等该 follower 的 LEO 大于等于该 Partition 的 HW,即 follower 追上 leader 之后,就可以重新加入 ISR 了。

(2)leader 故障

 leader 发生故障之后,会从 ISR 中选出一个新的 leader,之后,为保证多个副本之间的数据一致性,其余的 follower 会先将各自的 log 文件高于 HW 的部分截掉,然后从新的 leader同步数据。

注意:这只能保证副本之间的数据一致性,并不能保证数据不丢失或者不重复。

(3)Exactly Once 语义

  At Least Once 语义(至少一次):将服务器的ACK级别设置为-1,可以保证producer 到server之间的数据不丢失,即为At Least Once语义

   At Most Once语义(至多一次):将服务器ACK级别设置为0,可以保证生产者每条消息只被发送一次,即为 At Most Once语义

上面两种 语义,At Least Once 可以保证数据不丢失,但是不能保证数据不重复;相对的 At Most Once 能保证数据不重复,却不能保证数据不丢失。而 Exactly Once 语义,就是为了解决上面两种语义的弊端,既能保证数据不重复,又能保证数据不丢失(应用场景:一些交易信息数据)

Exactly Once 语义

引入了"幂等性" 的特性:所谓 "幂等性"就是值producer不论向server发送多少次重复的数据,server端只会持久化一条。幂等性 结合 At Least Once 语义,就构成了Exactly Once语义(即在保证数据完整的同时,去重处理)

At Least Once + 幂等性 = Exactly Once

要启用"幂等性",只需要将producer的参数中 enable.idompotence 设置为true即可。kafka 的幂等性 实现的其实就是将下游需要做去重的处理放在了数据上游。开启了幂等性的producer在初始化的时候会被分配一个PID,发往同一个Partition的消息会附带 Squence Number。而 Broker端会对做缓存,当具有相同主键的消息提交时,Broker只会持久化一条。

缺点:PID 重启就会变化,同时不同的 Partition 也具有不同主键,所以幂等性无法保证跨分区跨会话的 Exactly Once

消费者(Consumers)

通常来讲,消息模型可以分为两种, 队列和发布-订阅式。 队列的处理方式是 一组消费者从服务器读取消息,一条消息只有其中的一个消费者来处理。在发布-订阅模型中,消息被广播给所有的消费者,接收到消息的消费者都可以处理此消息。Kafka为这两种模型提供了单一的消费者抽象模型: 消费者组 (consumer group)。 消费者用一个消费者组名标记自己。 一个发布在Topic上消息被分发给此消费者组中的一个消费者。 假如所有的消费者都在一个组中,那么这就变成了queue模型。 假如所有的消费者都在不同的组中,那么就完全变成了发布-订阅模型。 更通用的, 我们可以创建一些消费者组作为逻辑上的订阅者。每个组包含数目不等的消费者, 一个组内多个消费者可以用来扩展性能和容错。

 

 2个kafka集群托管4个分区(P0-P3),2个消费者组,消费组A有2个消费者实例,消费组B有4个。

注:

 Kafka采用了一种分而治之的策略:分区。 因为Topic分区中消息只能由消费者组中的唯一一个消费者处理,所以消息肯定是按照先后顺序进行处理的。但是它也仅仅是保证Topic的一个分区顺序处理,不能保证跨分区的消息先后处理顺序。 所以,如果你想要顺序的处理Topic的所有消息,那就只提供一个分区。

1) 消费方式

     consumer采用 pull 的方式从broker中读取数据。(如果采用push的方式很难适应消费速率不同的消费者,因为发送的速率是由broker来决定的,它的目标是尽可能以最快的速度将消息传递,这样就容易造成处理性能慢的consumer来不及处理消息。典型的表现就是拒绝服务以及网络拥塞。)

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

2)分区分配策略(针对消费组)

      一个 consumer group 中有多个 consumer,一个 topic 有多个 partition,所以必然会涉及到 partition 的分配问题,即确定那个 partition 由哪个 consumer 来消费。

Kafka 有两种分配策略,一是 RoundRobin,一是 Range。

(1)RoundRobin 策略

      会将这个 consumer group 订阅的所有 topic 当成一个整体,对这个整体中的所有partition用hash算法进行转换后排序,之后 consumer group 中的消费者一一对应,轮询读取。由于 RoundRobin 策略,会将消费组订阅的所有topic中的partition打散后重新排序轮询。因此采用这种策略有一个前提条件 - - - 消费组中的所有消费者订阅的主题是一样的。

(2)Range策略(kafka默认的策略)

     按照主题进行划分的策略。(比方说有3个主题topic1-3,消费组中有3个消费者,消费者A订阅了topic1和topic2,消费者B订阅了topic2和topic3。公共订阅的topic会按partition数平均分配。但是同样存在弊端,如果共同订阅的topic有很多个并且里面的partition数是奇数,那么就可能导致消费者A被分配的partition原多于消费者B :因为是以每个topic中的partition进行平分,每个共同的topic分配给消费者A的就会多一个)

总结:消费组中的消费者订阅的topic相同,建议采用 RoundRobin 策略,可以尽可能的平均分配。如果消费组中的消费者订阅的topic不相同,就建议使用默认的range策略

3)offset 的维护

由于 consumer 在消费过程中可能会出现断电宕机等故障,consumer 恢复后,需要从故障前的位置的继续消费,所以 consumer 需要实时记录自己消费到了哪个 offset,以便故障恢复后继续消费。

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

 

Kafka的保证(Guarantees)

  • 生产者发送到一个特定的Topic的分区上,消息将会按照它们发送的顺序依次加入,也就是说,如果一个消息M1和M2使用相同的producer发送,M1先发送,那么M1将比M2的offset低,并且优先的出现在日志中。
  • 消费者收到的消息也是此顺序。
  • 如果一个Topic配置了复制因子(replication factor)为N, 那么可以允许N-1服务器宕机而不丢失任何已经提交(committed)的消息。

kafka中消费者组有两个概念:

队列:消费者组(consumer group)允许同名的消费者组成员瓜分处理。

发布订阅:允许你广播消息给多个消费者组(不同名)。

kafka的每个topic都具有这两种模式。

kafka通过并行topic的parition —— kafka提供了顺序保证和负载均衡。每个partition仅由同一个消费者组中的一个消费者消费到。并确保消费者是该partition的唯一消费者,并按顺序消费数据。每个topic有多个分区,则需要对多个消费者做负载均衡,但请注意,相同的消费者组中不能有比分区更多的消费者,否则多出的消费者一直处于空等待,不会收到消息

kafka作为一个存储系统

所有发布消息到消息队列和消费分离的系统,实际上都充当了一个存储系统(发布的消息先存储起来)。Kafka比别的系统的优势是它是一个非常高性能的存储系统

写入到kafka的数据将写到磁盘并复制到集群中保证容错性。并允许生产者等待消息应答,直到消息完全写入。

kafka的磁盘结构 - 无论你服务器上有50KB或50TB,执行是相同的。

client来控制读取数据的位置。你还可以认为kafka是一种专用于高性能,低延迟,提交日志存储,复制,和传播特殊用途的分布式文件系统

kafka的流处理

仅仅读,写和存储是不够的,kafka的目标是实时的流处理。

在kafka中,流处理持续获取输入topic的数据,进行处理加工,然后写入输出topic。例如,一个零售APP,接收销售和出货的输入流,统计数量或调整价格后输出。

可以直接使用producer和consumer API进行简单的处理。对于复杂的转换,Kafka提供了更强大的Streams API。可构建聚合计算连接流到一起的复杂应用程序。

助于解决此类应用面临的硬性问题:处理无序的数据,代码更改的再处理,执行状态计算等。

Streams API在Kafka中的核心:使用producer和consumer API作为输入,利用Kafka做状态存储,使用相同的组机制在stream处理器实例之间进行容错保障。

拼在一起

消息传递,存储和流处理的组合看似反常,但对于Kafka作为流式处理平台的作用至关重要。

像HDFS这样的分布式文件系统允许存储静态文件来进行批处理。这样系统可以有效地存储和处理来自过去的历史数据。

传统企业的消息系统允许在你订阅之后处理未来的消息:在未来数据到达时处理它。

Kafka结合了这两种能力,这种组合对于kafka作为流处理应用和流数据管道平台是至关重要的。

批处理以及消息驱动应用程序的流处理的概念:通过组合存储和低延迟订阅,流处理应用可以用相同的方式对待过去和未来的数据。它是一个单一的应用程序,它可以处理历史的存储数据,当它处理到最后一个消息时,它进入等待未来的数据到达,而不是结束。

同样,对于流数据管道(pipeline),订阅实时事件的组合使得可以将Kafka用于非常低延迟的管道;但是,可靠地存储数据的能力使得它可以将其用于必须保证传递的关键数据,或与仅定期加载数据或长时间维护的离线系统集成在一起。流处理可以在数据到达时转换它。

Kafka的使用场景:

(1)消息

(2)网站活动追踪

(3)指标

kafka也常常用于监测数据。分布式应用程序生成的统计数据集中聚合。

(4)日志聚合 elk+kafka

(5)流处理

Kafka Streams,还有Apache Storm和Apache Samza可选择。

(6)事件采集

事件采集是一种应用程序的设计风格,其中状态的变化根据时间的顺序记录下来,kafka支持这种非常大的存储日志数据的场景。

(7)提交日志

kafka可以作为一种分布式的外部日志,可帮助节点之间复制数据,并作为失败的节点来恢复数据重新同步,kafka的日志压缩功能很好的支持这种用法,这种用法类似于Apacha BookKeeper项目。


 

 


 

 

 

 



 


 


 

 

 

 

 

 

 

 

 

 

 

 

 

 

 




 



 

 

posted on 2022-07-01 15:36  让代码飞  阅读(128)  评论(0)    收藏  举报

导航

一款免费在线思维导图工具推荐:https://www.processon.com/i/593e9a29e4b0898669edaf7f?full_name=python