【年后跳槽必看篇】Kafka核心知识点-技术探秘第一章

2024-01-19 16:09:01 浏览数 (1)

基本概念

Kafka是一个成熟的消息队列,是一个天然分布式、支持分区(partition)、多副本(replica)。是基于Zookeeper协调的分布式消息系统。它最大特性就是可以实时的处理大量数据以满足各种需求场景:比如:基于Hadoop的批处理系统,低延迟的实时系统、storm/Spark流式处理引擎,web/nginx日志、访问日志,消息服务等等。Kafka使用Scala语言编写的。

Zookeeper用于维护Kafka集群的状态和元数据信息,例如主题和分区的分配信息、消费者组和消费者偏移量等。

关于Zookeeper可以参考我之前的文章了解:

【年后跳槽必看篇-非广告】一文带你吃透Zookeeper

Kafka的特性

  • 高吞吐量、低延迟:Kafka每秒可以处理几十万条消息,它的延迟最低只有几毫秒
  • 可扩展性:Kafka集群支持热扩展
  • 持久性、可靠性:消息被持久化到本地磁盘,并且支持数据备份防止数据丢失
  • 容错性:允许集群中节点失败(若副本数量为n,则允许n-1个节点失败)
  • 高并发:支持数千个客户端同时读写

Kafka的场景应用

  • 日志收集:一个公司可以使用Kafka收集各种服务的log,通过Kafka以通义接口服务的方式开放给各种consumer,例如Hadoop、Hbase、Solr等。
  • 消息系统:解耦、异步、削峰、分布式一致性等。关于为什么使用MQ(为什么使用消息队列)可参考文章:

对线面试官-为什么要使用MQ

  • 流式处理:比如:storm/Spark流式处理引擎

Kafka的架构是怎么样的

Kafka的架构是整体设计比较简单,是显示的分布式架构,主要由Producer(生产者)、broker(Kafka集群)、和consumer(消费者)组成。

如图所示:

Producer(生产者):生产者负责将消息发布到Kafka集群中的一个或多个Topic(主题)中,每个Topic包含一个或多个Partition(分区)

Topic:主题,是承载消息的逻辑容器,在实际使用中多用来区分具体的业务 Partition:分区,一个有序不变的消息序列。每个主题可以有多个分区

Consumer(消费者):消费者负责从Kafka集群中的一个或多个主题消费消息,并将消息的offset(偏移量)提交回Kafka以保证消息的顺序性和一致性。

偏移量:offset,表示分区中每条消息的位置信息,是一个单调递增且不变的值

Kafka集群:Kafka集群是由多个Kafka节点(Broker)组成的分布式系统。每个节点都可以存储一个或多个Topic(主题)的Partiton(分区副本),以提高可用性和容错能力

Leader Broker:Leader Broker是分区的副本,它是负责处理消息读写请求的节点。生产者将消息发送到LeaderBroker,消费者从Leader Broker中拉取消息 Follower Broker:Follower Broker是Leader Broker的备份节点,它负责与LeaderBroker进行数据同步,以保持自己的数据与Leader Broker一致 在集群中,每个分区都有一个Leader Broker和多个Follower Broker,只有Leader Broker才能处理生产者和消费者的请求,而Follower Broker只是Leader Broker的备份,用于提供数据的冗余备份和容错能力。如果Leader Broker发生故障,Kafka集群将会自动将Follower Broker提升为新的Leader Broker,从而实现高可用性和容错能力

架构图:

image.png

Kafka中比较重要的思想

  • ConsumerGroup(消费者群组):各个消费者consumer可以组成一个组,每个消息中只能被族中的一个consumer消费,如果一个消息可以被多个consumer消费的话,那么这些consumer必须在不同的组
  • 消息状态:在Kafka中,消息的状态被保存在consumer中,broker不会关心哪个消息被消费了或被谁消费了,只记录一个offset值(指向partition中下一个要被消费的消息位置),这就意味着如果consumer处理不好的话,broker上的一个消息可能会被消费多次
  • 消息持久化:Kafka会把消息持久化到本地文件系统中,并且保持极高的效率
  • 消息有效期:Kafka会长久保留其中的消息,以便consumer可以多次消费,当然其中很多细节是可配置的
  • 批量发送:Kafka支持以消息集合为单位进行批量发送,以提高push效率
  • push-and-pull:Kafka中的producer和consumer采用的是push-and-pull模式,即producer只管向broker push消息,consumer只管从broker中pull消息。两者对消息的生产和消费是异步的
  • Kafka集群中broker之间的关系:不是主从关系,各个broker在集群中的地位是一样的,我们可以随意的增加或删除任何一个broker节点。
  • 负载均衡方面:Kafka提供了一个metadata API来管理broker之间的负载(仅对Kafka 0.8.x 而言,对于0.7.x主要是靠zookeeper来实现负载均衡)
  • 同步异步:producer采用异步push方式,极大提高了Kafka系统的吞吐量(并且可以通过参数控制是采用同步还是异步方式)
  • 分区机制partition:Kafka的broker端支持消息分区,producer可以决定把消息发送到哪个分区,在一个分区中消息的顺序就是producer发送消息的顺序,一个主题中可以有多个分区(partition),具体分区数量也是可配置的。分区的意义很大。
  • 离线数据装载:Kafka由于对可扩展的数据持久化的支持,它也非常适合向Hadoop或者数据仓库中进行数据装载
  • 插件支持:现在已经有不少活跃社区开发出很多插件来支持扩展Kafka的功能,如用来配合Storm、Hadoop、Flume等相关的插件。

当我们需要自己设计一个MQ的时候也可以从上述比较好的思想中提炼出我们所需要的:

关于如何写一个消息队列,该如何进行架构设计,可参考文章:

场景题-如果让你写一个消息队列,该如何进行架构设计啊?说一下你的思路。

Kafka为什么这么快

消息发送方面:

  1. 批量发送:Kafka通过将多个消息大巴拼成一个批次,减少了网络传输和磁盘写入的次数,从而提高了消息的吞吐量和传输效率
  2. 异步发送:生产者可以异步发送消息,不必等待每个消息的确认,这大大提高了消息发送的效率
  3. 消息压缩:支持对消息进行压缩,减少网络传输的数据量
  4. 并行发送:通过将数据分别不在不同的分区(Partitions),生产者可以并行发送这些消息,从而提高了吞吐量

消息存储方面:

  1. 零拷贝技术:Kafka使用零拷贝技术来避免了数据的拷贝操作性能问题,降低了内存和CPU的使用率,提高了系统的性能
  2. 磁盘顺序写入:Kafka把消息存储在磁盘上,且以顺序的方式写入数据。顺序写入比随机写入速度快很多,因为它减少了磁头寻道时间。避免了随机读写带来的性能损耗,提高了磁盘的使用效率
  3. 页缓存:Kafka将其数据存储在磁盘中,但在访问数据时,它会先将数据加载到操作系统中的页缓存中,并在页缓存中保留一份副本,从而实现快速的数据访问。
  4. 稀疏索引:Kafka存储消息是通过分段的日志文件,每个分段都有自己的索引。这些索引文件中的条目不是对分段中的每条消息都建立索引,而是每隔一定数量的消息建立一个索引点,这就构成了稀疏索引。稀疏索引减少了索引大小,使得加载内存中的索引更小,提高了查找特定消息的效率
  5. 分区和副本:Kafka采用分区和副本的机制,可以将数据分散到多个节点上进行处理,从而实现了分布式的高可用性和负载均衡

消息消费方面:

  1. 消费者群组:通过消费者群组可以实现消息的负载均衡和容错处理
  2. 并行消费:不同的消费者可以独立地消费不同的分区,实现消费的并行处理
  3. 批量拉取:Kafka支持批量拉取消息,可以一次性拉取多个消息进行消费。减少网络消耗,从而提升性能

Kafka如何保证消息不丢失

正常情况下,消息丢失大概分为三种情况:

  1. 生产者消息丢失(Producer端发送消息到Kafka Broker时丢失)
  2. Kafka(MQ)本身将消息弄丢了(Kafka 处理消息进行同步持久化时失败)
  3. 消费者消费的时候消息丢失(Consumer从Kafka Broker端拉取数据进行消费出现异常)

注意:Kafka只对已提交的消息做最大限度地持久化保证不丢失,但是办法保证100%。后面会讲

Producer(生产者)角度

消息的生产者,消息发送给Kafka集群的过程中有可能会出现异常失败。所以需要有机制来确保消息能够成功发送。(但是还是存在网络波动的问题无法保证一次消息一定能发送成功)。如果没有从成功需要重新发送知道成功。

我们在使用Kafka发送消息的时候,通常使用的时producer.send(msg)来发送消息,这是一种异步发送,发送消息的时候方法会立即返回,但不一定代表消息发送成功了。当时方法prodcuer.send(msg).get()是同步等待返回的。

所以我们通常为了保证消息在发送不丢失,会建议使用producer.send(msg, callback)方法,这个方法支持传入一个callback,我们可以在消息发送的时候进行重试。同时Producer还提供了一些配置参数来提升发送成功率:

代码语言:javascript复制
acks=-1  # 或者 acks=all 该参数表示Leader 和 Follower都接受成功时确认,可以最大限度保证消息不丢失,但是吞吐量低。
retries=3 # 重试次数,也可以设置为max ,一旦失败就会无限重试,卡在这里。
retry.backoff.ms = 300  # 消息发送超时或失败后,间隔的重试时间

acks = 0:表示Producer请求立即返回,不需要等待Leader的任何确认。这种方案有最高的吞吐率,但是不保证消息是否真的发送成功 acks = -1:等价于(acks = all),表示分区Leader必须要等待消息被成功写入到所有的ISR副本(同步副本)中才认为Producer请求成功。这种方案提供最高的消息持久性保证,但是理论上吞吐率也是最差的。 acks = 1:表示Leader副本必须应答此Producer请求并写入消息到本地日志,之后Producer请求被认为成功。如果此时Leader副本应该请求之后挂掉了,消息会丢失。这个方案,提供了不错的持久性保证和吞吐

Kafka本身丢失消息(broker集群)

在实际业务场景会存在Kafka的Leader接收到了消息,但是还没有来得及同步给Follower就挂掉了,此时Follower变成了Leader,导致数据丢失。

在Kafka集群中有一些机制来保证消息的不丢失,比如:复制机制、持久化存储机制以及ISR机制。

  • 持久化存储:Kafka使用持久化存储来存储消息。这意味着消息在写入Kafka时将被写入磁盘,这种方式可以防止消息因为节点宕机而丢失。
  • ISR复制机制:Kafka使用ISR机制来确保消息不会丢失,Kafka使用复制机制来保证数据的可靠性。每个分区都有多个副本,副本可以分布在不同的节点上。当一个节点宕机时,其它节点上的副本仍然可以提供服务,保证消息不丢失。

当然在Kafka中还提供了一些配置参数来避免消息丢失的问题:

代码语言:javascript复制
replication.factor  # 表示分区副本数量,replication.factor >1 当Leader副本挂了,Follower副本会被选举为Leader继续提供服务

min.insync.replicas # 表示 ISR 最少副本的数量,通常设置min.insync.replias > 1 这样才能有可用的follower副本执行替换,保证消息不丢失

unclean.leader.election.enable = false  # 是否可以把非 ISR 集合中的副本选举为 Leader副本

消费者角度消息丢失(Consumer)

消费者消费消息的时候,消息还没有处理完成,便自动提交了offset。导致消息没有消费丢失掉。

所以就需要保证不要乱提交offset就行了。在这方面Kafka消费者会跟踪每个分区的offset(偏移量),消费者每次消费消息时,都会将offset向后移动。当消费者宕机或者不可用时,Kafka会将该消费者所消费的分区的offset保存下来,下次该消费者重新启动时,可以从上一次offset重新开始消费

另外,Kafka消费者还可以组成消费者组,每个消费者组可以同时消费多个分区。当一个消费者组中的消费者宕机或者不可用时,其他消费者仍然可以消费该组的分区,保证消息不丢失。

同时也可以关闭自动提交offset,去手动提交offset,避免拉取了消息以后,业务逻辑没处理完,提交偏移量后但是消费者挂了的问题:

代码语言:javascript复制
enable.auto.commit=false

好了,本章节到此告一段落。希望对你有所帮助,祝学习顺利。

0 人点赞